Operations 11 min read

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.

Code of Duty
Code of Duty
Code of Duty
Global Nginx Container Setup: Public Network, Config & Startup Validation

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/coduty

The 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: true

Key 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-nginx

If 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 reload

Skipping 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:8000

Common 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.conf

Adding 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-nginx

Later, 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerdeploymentConfiguration ManagementtroubleshootingNginxReverse ProxyDocker ComposeDocker Networking
Code of Duty
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.