Global Nginx Container Setup: Public Network, Config & Startup Validation
A step-by-step guide to deploying a global Nginx reverse proxy using Docker Compose, covering directory layout, external Docker network creation, minimal configuration, container startup, config testing, reload procedures, access verification, and troubleshooting common issues for multi-project ingress.
This article is part 24 of a series on deploying services to the internet. It implements a global Nginx container that serves as a unified public entry point for multiple projects, listening only on ports 80 and 443 and routing by domain name.
Prerequisites
Before starting, the server must have:
A regular user login (e.g., coduty)
Docker and Docker Compose installed and working
Cloud security group and system firewall allowing ports 80 and 443
No existing service occupying ports 80/443 (verify with sudo ss -lntp | grep -E ':80|:443')
Directory Structure
Create a fixed layout for configuration, certificates, and logs:
sudo mkdir -p /opt/coduty/nginx/conf.d
sudo mkdir -p /opt/coduty/nginx/certs
sudo mkdir -p /opt/coduty/nginx/logs
sudo chown -R coduty:coduty /opt/codutyThe structure is:
Nginx has an independent directory; config in conf.d , certs in certs , logs in logs , Compose file at the root.
Public Docker Network
Create an external network so the global Nginx can reach project containers by service name: docker network create coduty-public Each project keeps its own default internal network. Only the application containers that need to be proxied join coduty-public; database containers stay isolated.
Docker Compose for Global Nginx
File:
/opt/coduty/nginx/docker-compose.yml services:
nginx:
image: nginx:alpine
container_name: global-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./conf.d:/etc/nginx/conf.d:ro
- ./certs:/etc/nginx/certs:ro
- ./logs:/var/log/nginx
networks:
- coduty-public
networks:
coduty-public:
external: trueKey parameters: container_name: fixed name for easy docker exec commands restart: unless-stopped: auto-restart on failure or host reboot ports: only 80 and 443 exposed conf.d and certs mounted read-only to prevent accidental changes logs mounted to host for inspection and backup coduty-public: the external network created earlier
Minimal Nginx Configuration
Create /opt/coduty/nginx/conf.d/default.conf to verify the entry point works:
server {
listen 80;
server_name _;
location / {
return 200 "global nginx is running
";
}
}This minimal config does not proxy any project or handle HTTPS; it only confirms Nginx starts, port 80 is reachable, and firewall/security group rules are correct.
Start and Verify
cd /opt/coduty/nginx
docker compose up -d
docker ps
docker logs global-nginxIf startup fails, check in order: port occupancy, network existence, mount directories, config syntax. Use docker compose logs when the container is not running.
Configuration Check and Reload
Always test before reloading:
docker exec global-nginx nginx -t
docker exec global-nginx nginx -s reloadSkipping nginx -t risks breaking multiple sites once real projects are added.
Access Verification
With server IP 1.2.3.4: curl http://1.2.3.4 Expected response: global nginx is running. If external access fails but curl http://127.0.0.1 works, the issue is likely security group, firewall, or public routing. If local also fails, inspect container status, port binding, and Nginx config.
Future Project Integration Example
For a blog app container named blog-app listening on port 8000 and joined to coduty-public:
server {
listen 80;
server_name blog.example.com;
location / {
proxy_pass http://blog-app:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Test connectivity from inside the Nginx container:
docker exec -it global-nginx sh
wget -qO- http://blog-app:8000Common failure points: project container not running, not on coduty-public, wrong service name, app listening on 127.0.0.1 instead of 0.0.0.0.
Configuration File Organization
Split configs per site under conf.d/:
conf.d/
default.conf
blog.aicultiv.com.conf
www.aicultiv.com.conf
api.aicultiv.com.confAdding a project means adding a file; removing means deleting a file. HTTPS redirects and certificate config go in the respective site file.
Log Management
Logs are mounted to /opt/coduty/nginx/logs on the host:
ls -lh /opt/coduty/nginx/logs
docker logs global-nginxLater, split logs per site in each config:
access_log /var/log/nginx/blog.access.log;
error_log /var/log/nginx/blog.error.log;Common Troubleshooting
Port occupied: use ss to identify the process; do not randomly change ports.
Nginx cannot resolve project container: run docker network inspect coduty-public to confirm both containers are attached.
Config changes not taking effect: run nginx -t then reload; verify mount paths.
External access fails: trace layer by layer — DNS, security group, firewall, Nginx container, port listening, domain match, upstream service, Nginx logs.
At this stage the global Nginx lacks HTTPS and real project proxying, but the foundation is solid: clear directory structure, public network exists, container starts, config can be validated, logs are accessible, and future projects can integrate via a fixed pattern.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code of Duty
"Code of Duty" — Every line of code has its own mission. We avoid shortcuts and quick fixes, focusing on authentic coding reflections and the joys and challenges of technical growth. The journey of learning matters more than any destination. Join us as we humbly forge ahead in the world of code.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
