Hosting multiple static sites on one container
/ 2 min read
Table of Contents
The typical use-case
The docker.io/nginx image is great for hosting static files. It’s as simple as:
services: nginx-site1: image: nginx volumes: - ./mounts/html1:/usr/share/nginx/html:ro nginx-site2: image: nginx volumes: - ./mounts/html2:/usr/share/nginx/html:roThis works, but for my kind of traffic there’s no point in having 4 nginx containers running when 1 would be more than enough.
Putting it all in a single container
With a little bit of nginx configuration, we can host multiple sites on one container and route trafik to them using the subdomains (i.e. Layer 7 routing). The docker compose file barely changes:
services: nginx-multi: image: docker.io/nginx:stable-alpine volumes: - ./mounts/extra-servers.conf:/etc/nginx/conf.d/extra-servers.conf:ro # Content to host - ./mounts/html-A:/usr/share/nginx/html/A:ro - ./mounts/html-B:/usr/share/nginx/html/B:ro # ...# A.imochoa.com (and C.imochoa.com)server { listen 80; listen [::]:80; server_name A.imochoa.com C.imochoa.com
root /usr/share/nginx/html/A; index index.html index.htm index.php;
}
# B.imochoa.comserver { listen 80; listen [::]:80; server_name B.imochoa.com;
root /usr/share/nginx/html/blog; index index.html index.htm index.php;}…is this actually better?
If you do not expect a lot of traffic you can just save resources and handle everything in a small container :)
However… you still need to forward requests to it. With traefik as a reverse proxy, that looks something like this:
services: nginx-multi: image: docker.io/nginx:stable-alpine volumes: - ./mounts/extra-servers.conf:/etc/nginx/conf.d/extra-servers.conf:ro # Content to host - ./mounts/html-A:/usr/share/nginx/html/A:ro - ./mounts/html-B:/usr/share/nginx/html/B:ro # ... labels: - traefik.enable=true - traefik.http.routers.nginx-multi.rule=( Host(`A.imochoa.com`) || Host(`B.imochoa.com`) || Host(`C.imochoa.com`) ) - traefik.http.routers.nginx-multi.entrypoints=https-external - traefik.http.routers.nginx-multi.tls.certresolver=myresolver - traefik.http.routers.nginx-multi.tls=trueThe downside is you have to split the routing between traefik and the nginx config file in the container, which feels a bit muddy.
You can recover some of the muddyness by splitting the routes into different traefik routers so that you can treat them differently (e.g. defining different traefik routers for each one)
labels: - traefik.enable=true # Blog - traefik.http.routers.nginx-blog.rule=( Host(`www.imochoa.com`) || Host(`blog.imochoa.com`) ) - traefik.http.routers.nginx-blog.entrypoints=https-external - traefik.http.routers.nginx-blog.tls.certresolver=myresolver - traefik.http.routers.nginx-blog.tls=true # Talon - traefik.http.routers.nginx-talon.rule=( Host(`talon.imochoa.com`) ) - traefik.http.routers.nginx-talon.entrypoints=https-external - traefik.http.routers.nginx-talon.tls.certresolver=myresolver - traefik.http.routers.nginx-talon.tls=trueUltimately you still depend on the nginx container to handle the hostnames, acting as a sort of reverse-proxy behind the reverse-proxy, so I would recommed a setup like this outside of a homelab.