To serve a web host via NGINX while routing specific subdomains directly to a Docker server on a custom port, configure NGINX as a reverse proxy using distinct server_name blocks. This approach isolates traffic for each subdomain, ensuring that requests are forwarded to the correct internal container IP and port without affecting other services or requiring wildcard DNS entries.
Understanding the Architecture
The core challenge in this setup is managing multiple services behind a single public IP address. NGINX acts as the entry point, listening on standard HTTP (port 80) and HTTPS (port 443) ports. It inspects the incoming request's Host header to determine which backend service should handle the traffic. For the main domain or general web hosting, NGINX serves static files or proxies to a primary application. For specific subdomains, such as app.example.com, NGINX forwards the request to a Docker container running on a non-standard port, like 8080 or 3000.
This architecture requires careful configuration of both the Docker networking and the NGINX virtual hosts. The Docker container must be exposed to the host machine’s network interface so NGINX can reach it. Meanwhile, NGINX must be configured with precise location blocks to ensure that only the intended subdomain traffic is proxied to the Docker instance. Incorrect configurations can lead to 502 Bad Gateway errors or security vulnerabilities where unauthorized access to internal services occurs.
Configuring Docker Networking
Before configuring NGINX, ensure your Docker container is accessible from the host system. When running your Docker container, you need to map the internal port to a port on the localhost interface. Use the -p flag when starting the container. For example:
docker run -d --name my-app -p 127.0.0.1:8080:80 my-image
Binding to 127.0.0.1 is crucial for security. It ensures that only processes on the same machine (like NGINX) can connect to the Docker container. If you bind to 0.0.0.0, the port becomes accessible from any external IP, bypassing NGINX’s protection. After starting the container, verify connectivity by running curl http://127.0.0.1:8080. You should receive the response from your application. This step confirms that the Docker side of the equation is correctly set up before touching the web server configuration.
Setting Up NGINX Virtual Hosts
NGINX uses server blocks to define how different domains and subdomains are handled. Create a new configuration file in the /etc/nginx/sites-available/ directory. A typical configuration for a subdomain proxy looks like this:
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}
The server_name directive explicitly matches the subdomain. This prevents wildcard conflicts where one rule might accidentally catch traffic meant for another service. The proxy_pass directive sends the request to the Docker container. The proxy_set_header lines preserve important client information, such as the original IP address and hostname, which many applications rely on for logging and security checks. After saving the file, create a symbolic link in sites-enabled and reload NGINX using sudo nginx -t to test syntax, followed by sudo systemctl reload nginx.
Handling HTTPS and Security
In production environments, serving content over HTTP is insufficient. You should implement HTTPS using Let’s Encrypt or a similar certificate authority. Tools like Certbot automate the process of obtaining and renewing SSL certificates. When configuring NGINX for HTTPS, ensure that the listen 443 ssl directive is present along with paths to your certificate and key files. Additionally, consider redirecting all HTTP traffic to HTTPS to maintain security standards.
If you are managing multiple subdomains, consider using a shared configuration snippet for common proxy settings to reduce redundancy. This approach minimizes configuration errors and makes maintenance easier. For more complex setups involving dynamic upstream servers, explore advanced load balancing techniques within NGINX. See Setting up Nginx Traffic Redirect for Subdomains on VPS with Multiple Docker Containers and Cloudflare for insights on handling traffic redirection in multi-container environments.
Troubleshooting Common Issues
If you encounter a 502 Bad Gateway error, check if the Docker container is running and listening on the expected port. Verify that NGINX can reach 127.0.0.1:8080 using curl. If the connection times out, ensure that firewall rules allow local loopback traffic, although this is rarely an issue on localhost. Another common problem is incorrect header forwarding. Some frameworks require specific headers to function correctly. Always include X-Real-IP and X-Forwarded-Proto to ensure compatibility with modern web applications.
Additionally, monitor NGINX logs at /var/log/nginx/error.log for detailed error messages. These logs often provide specific reasons why a proxy connection failed, such as permission denied or connection refused. Regularly updating NGINX and Docker versions helps mitigate known vulnerabilities and improves performance stability.
Frequently Asked Questions
Why shouldn't I use wildcard DNS for subdomains?
Wildcard DNS records catch all subdomains, including those you did not intend to route. This can lead to security risks and conflicts with other services that may use the same domain space. Explicitly defining server names in NGINX provides better control and isolation.
How do I debug a 502 Bad Gateway error in NGINX?
Check if the target service is running and accessible via localhost. Verify NGINX error logs for specific failure reasons. Ensure that the proxy_pass URL matches the exact port and protocol used by your backend service.
Can I run multiple Docker containers behind one NGINX instance?
Yes, NGINX can manage multiple backend services by using different server blocks or location paths. Each subdomain or path can be routed to a different container port, allowing efficient resource utilization.
What is the purpose of proxy_set_header directives?
These directives pass client information like IP addresses and original hostnames to the backend server. Many applications rely on this data for logging, analytics, and security checks to function correctly.
Is it safe to expose Docker ports directly to the internet?
No, exposing Docker ports directly bypasses NGINX’s security features and logging. Always bind Docker ports to localhost and let NGINX handle external traffic to protect your infrastructure.
How do I renew SSL certificates automatically?
Use tools like Certbot with automatic renewal hooks. Configure systemd timers or cron jobs to check for certificate expiration and trigger renewal when necessary, ensuring continuous HTTPS availability.