Dockerizing Nginx
12/06/25
Loose Ends
Now I had all of my main original goals achieved for this website in my homelab, it was locally accessible, publically accessible, and easily updateable with my CI/CD in place. However, things as they are are a bit all over the place. First of all, I access the site locally at http://192.168.1.5 (my server) with an Nginx reverse proxy that proxies to http://127.0.0.1:8080 (the website's container exposed to the host). But second of all however, Cloudflare accesses the container over the dedicated Docker network I set up for the tunnel. I had Nginx running on bare-metal but I figured it would be cleaner if I Dockerized Nginx so that the Cloudflare tunnel could access the site over a Docker network, and I would just access the container directly locally. This would also prove to more scalable as I was planning to host other sites at this point, namely my portfolio.
Dockerization
I figured that it would be easiest to add an Nginx service to my Docker Compose file in my homelab directory/repo where I defined the Cloudflare service. This would create one holistic "web" service allowing me to access this site's container as well as others more cleanly with Nginx reverse proxies externally. I added the following service and network definitions to the Docker compose file in order to achieve this:
nginx:
image: nginx:alpine
container_name: nginx
restart: unless-stopped
networks:
- cloudflare-tunnel-net
- nginx-proxy-net
volumes:
- ../../config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ../../config/nginx/sites:/etc/nginx/sites-enabled:ro
- ../../logs/nginx:/var/log/nginx
networks:
cloudflare-tunnel-net:
nginx-proxy-net:
external: true
I changed the original cloudflare-tunnel-net
network to no longer be external so that it's only responsible for connecting the Cloudflare tunnel and Nginx. Instead, now I have the external nginx-proxy-net
network which is responsible for facilitating the reverse proxies to website containers elsewhere (I will be changing their setups to use this network).
Now in theory, the flow of HTTP requests through the tunnel could access the appropriate container, since I mapped the domain name in the Cloudflare dashboard to http://nginx which would be new the service URL in the tunnel.
Creating this setup also taught me a bit about how Docker networks work in more detail. Networks not defined as external in a Docker Compose file will be automatically created when you bring up the containers and only accessible to services in the same Docker Compose projects, i.e cloudflare-tunnel-net
. Networks defined otherwise have to be manually created before Docker can bring them into the Compose. Defining them as external also allows services in other Docker Compose projects to use them, i.e nginx-proxy-net
(these networks live outside the Compose lifecycle).
I then created nginx-proxy-net
with docker network create nginx-proxy-net
. Now everything was in place in order to Dockerize Nginx and update my setup.
Site Configuration & Starting Containers
Firstly, in order to gain access to my site over my private network, I binded its container port 80 to host port 3001 in its Docker Compose file on the server. This was now instead of accessing it via the bare-metal Nginx reverse proxy setup that I would be removing. I then updated my firewall to allow forwarding to any port 80 so that the container could be accessible. This was achieved with the following: sudo ufw route allow 80/tcp
. I restarted the firewall and container, and then navigated in the browser to http://192.168.1.5:3000. The site showed up so internal access was in order.
Next, I updated my site's docker-compose.yml
to use the new external nginx-proxy-net
network (for Nginx access) instead of the old cloudflare-tunnel-net
network which was repurposed. I then changed the container name and restarted the container. The container would now be accessible over nginx-proxy-net
via http://blog, which we would use in the Nginx site configuration.
My Nginx container would be using the config/nginx/sites
directory in my local repo as a bind mount for the Nginx virtual host definitions. So I created a blog.conf
file in the directory which would be the site configuration used to reverse proxy to the container. This is what the configuration ended up looking like:
server {
listen 80;
server_name homelab.nicolldouglas.dev;
location / {
proxy_pass http://blog;
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;
}
}
With this, any incoming HTTP requests from the tunnel where the "Host" header matched the server_name
directive would be reverse proxied to http://blog via the nginx-proxy-net
network.
Now it was time to start my Nginx-Cloudflare setup. I ran docker compose up
, checked that the containers were running as well as the logs. In my Cloudflare dashboard, the tunnel was also shown as online. All seemed fine so I navigated to https://homelab.nicolldouglas.dev to see if my site was accessible, and indeed it was so the setup was fully functional for external access.
With that, I had successfully Dockerized Nginx and cleaned up the setup and access of my web services. Below is a diagram of how the archicture looks now:
