Demystifying the wadhwas.org Infrastructure: A Docker-Powered Nginx Setup
As a Site Reliability Engineer, I spend my days managing infrastructure at planetary scales. However, applying the same principles of isolation, reliability, and security to my own home server and family projects is just as rewarding. In this post, I'll walk you through the reverse proxy setup that makes all components of the wadhwas.org ecosystem run smoothly.
The Problem: Multiple Services, Single Ingress
The wadhwas.org network hosts several distinct projects: a Nextcloud cloud instance, my portfolio and blog, my wife's portfolio, and our interactive family tree. All of these require secure HTTPS access over standard ports (80 and 443), but they are run using different stacks and runtimes.
To solve this, I designed a containerized infrastructure using Nginx as an ingress controller. Nginx acts as a reverse proxy, terminating SSL, evaluating the host headers, and routing traffic to the correct container or serving static assets.
Network Topology & Container Layout
The architecture centers around a custom Docker bridge network called nginx-network. This isolates our containers from the host network while enabling secure container-to-container communication.
[ Host Ingress (80/443) ]
│
▼
┌──────────────────────────────────────┐
│ Nginx Reverse Proxy Container │
│ (Ports 80/443 mapped to host) │
└──────────┬──────────────┬────────────┘
│ │
│ proxy_pass │ direct root serving
▼ ▼
┌──────────────────┐ ┌──────────────────────────────────────────────┐
│ Nextcloud App │ │ Local Directory Volume Mounted │
│ Container │ │ (/etc/nginx/conf.d/) │
│ │ ├──────────────────┬──────────────┬────────────┤
└──────────────────┘ │ /tanmay │ /WFT │ /anushka │
│ (My Portfolio) │ (Family Tree)│ (Portfolio)│
└──────────────────┴──────────────┴────────────┘
The Nginx proxy container is started within the bridge network so it can reliably talk to other backend services:
-v $(pwd):/etc/nginx/conf.d \
--network=nginx-network \
--name nginx-reverse-proxy --restart always nginx:latest
By mounting the local workspace folder to /etc/nginx/conf.d, Nginx reads our configuration files directly from the host. This mount also lets Nginx serve static assets for the other projects directly from directories in the repository without the overhead of additional container runtimes.
Breaking Down the Nginx Server Blocks
Our nginx.conf is configured with distinct server blocks. Let's look at how the different services are routed.
1. Global HTTP to HTTPS Redirection
For security and SEO integrity, we redirect all incoming HTTP (port 80) traffic to its HTTPS equivalent. Nginx catches all domains in one listener block:
server {
listen 80;
server_name www.cloud.wadhwas.org cloud.wadhwas.org
tanmaywadhwa.com www.tanmaywadhwa.com www.tanmay.wadhwas.org tanmay.wadhwas.org
anushkajain.wadhwas.org www.anushkajain.wadhwas.org
familytree.wadhwas.org www.familytree.wadhwas.org;
location / {
return 301 https://$host$request_uri;
}
}
2. Nextcloud Storage Proxy (cloud.wadhwas.org)
Nextcloud runs inside a separate container on the private network. Since we use it for backup and file transfers, we must set a generous client body size to allow large file uploads:
server {
listen 443 ssl;
server_name www.cloud.wadhwas.org cloud.wadhwas.org;
ssl_certificate /etc/nginx/conf.d/certs/cert_nc.pem;
ssl_certificate_key /etc/nginx/conf.d/certs/privkey_nc.pem;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 20G; // Support uploads up to 20GB
location / {
proxy_pass http://nextcloud-container;
proxy_set_header Host $host;
}
}
3. Serving Static Portfolios & Projects
For the other domains (my portfolio/blog, my wife's portfolio, and the family tree), we do not need backend application servers. Nginx serves these static files directly from our mounted volume paths, optimizing load times and keeping resource consumption to an absolute minimum:
# Serving Tanmay's Site & Blog (tanmay.wadhwas.org / tanmaywadhwa.com)
server {
listen 443 ssl;
server_name tanmaywadhwa.com www.tanmaywadhwa.com www.tanmay.wadhwas.org tanmay.wadhwas.org;
ssl_certificate /etc/nginx/conf.d/certs/cert_tw.pem;
ssl_certificate_key /etc/nginx/conf.d/certs/privkey_tw.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /etc/nginx/conf.d/tanmay;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# Serving Wadhwa Family Tree (familytree.wadhwas.org)
server {
listen 443 ssl;
server_name familytree.wadhwas.org www.familytree.wadhwas.org;
ssl_certificate /etc/nginx/conf.d/certs/cert_ft.pem;
ssl_certificate_key /etc/nginx/conf.d/certs/privkey_ft.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /etc/nginx/conf.d/WFT;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
SSL Automation and Renewal Pipeline
Enforcing SSL is only half the battle; manual certificate renewal is a major source of operational toil. To solve this, I wrote an automated pipeline using Let's Encrypt's certbot and a script to coordinate the cert update flow.
Since Certbot's standalone mode requires port 80 to verify domain ownership, our script takes the following steps:
- Stop the Nginx reverse proxy container to release ports 80 and 443.
- Execute
certbot renewon the host system to fetch updated certificates. - Copy and rename the renewed certs/keys (renamed to
cert_*.pemandprivkey_*.pem) into the local project'scerts/directory. - Trigger our Docker refresh script to destroy the old container instance and start a new container with the refreshed keys.
"Automating the certificate cycle mitigates the risk of expired TLS certificates—a common failure mode in both personal labs and corporate production networks."
Conclusion
By combining Docker networks, Nginx virtual host routing, and Let's Encrypt automation, we've created a zero-maintenance ingress stack for wadhwas.org. It's performant, lightweight, and ensures all of our applications remain secure by default.