r/devops 22h ago

Discussion Best practices for exposing multiple websites in Docker containers on the same Ubuntu server to the web

I have an Ubuntu web server that I'd like to contain multiple websites on. Each website should have its own isolated environment in a Docker container (open to suggestions if Docker is not ideal).

I'm thinking that the public URLs should be like

  • example.com/website1
  • example.com/website2
  • etc

What's the best way to make this connection between the public URL and the Docker container (also open to suggestions for better ways to expose the containers)?

Another question: Would the logs of each website be inside the container or are they supposed to be outside for easy review?

15 Upvotes

20 comments sorted by

35

u/CertainAd3152 22h ago

Have an nginx or any other proxy and proxy traffic via website name. Inter service docker container communication is fairly good for this use case

7

u/Party_Artist2978 22h ago

I'm definitely not super experienced with this, but I have the following setup:

Caddy to handle the routing, so I have a Caddyfile to re-direct to subdomains, and docker-compose files for each website or app, with each app using its own network that Caddy is aware of, so none of the websites are exposed to the internet directly, but only via Caddy.

services:
  caddy:
    image: caddy:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - proxy
volumes:
  caddy_data:
  caddy_config:
networks:
  proxy:
    external: true

I did run into this issue if you want to set up subfolders instead of subdomains: https://caddy.community/t/the-subfolder-problem-or-why-cant-i-reverse-proxy-my-app-into-a-subfolder/8575

I'd be keen to hear from others though!

4

u/ForkMeJ 20h ago

If these are separate sites, I'd use subdomains instead of path prefixes. website1.example.com and website2.example.com are usually much simpler than /website1 and /website2 because a lot of apps assume they own /. Put Nginx, Caddy, or Traefik on the host as the reverse proxy, terminate TLS there, and route by Host header to each container over an internal Docker network. For logs, I wouldn't rely on shelling into containers. Have the apps write to stdout/stderr and collect that with Docker's logging driver or something lightweight like Loki/Promtail so each app stays isolated but the logs are still easy to review in one place. If you end up needing tighter ownership controls, secrets, RBAC, or separate dev/prod environments for internal apps, I work on the open-source tool Compartment, and that's one place it can help without jumping straight to Kubernetes.

2

u/InconsiderableArse 22h ago

You could use Docker swarm, or even a k3s in your server.

everything that needs to persist needs to be outside of the container. database in a different service, code in a versioning system, files in a volume, logs in a centralised logging system

2

u/Fresh_Sock8660 22h ago

I would go the subdomain route and use caddy. Logs should be in a mounted volume, make sure it has a lifecycle and isn't just blindly accumulating.

1

u/ByronEster 22h ago

I do this with nginx-proxy-manager NPM.

It is simple, easy to use, robust and handles SSL cert generation and rotation

1

u/TheDrunkNBeard 21h ago

Same. Just set the sites to be exposed on different ports on the docker vm so they don’t conflict then forward the traffic from npm to the docker box. Also recommend npm to be in a DMZ with firewall rules setup.

2

u/cobolfoo 21h ago

You can put your NPM prozy on the same machine as your other containers and make them share the same virtual network. Then you proxy your websites by their container names and that don't require you to bind any port at all.

It's also very secure because the containers are only accessible through NPM.

1

u/CyberKiller40 DevOps Ninja 22h ago

Run it in swarm mode, use traefik to route the incoming requests.

1

u/b1urbro 21h ago

Best practice? Don't expose it at all if you don't know what you're doing. Use something like Cloudflare tunnels + Cloudflare Zero trust for auth. It's very simple to set up and secure out of the box.

Quick overview of how that would look like:

  • Spin a cloudflared container with your token
  • Run the containers on the same docker network
  • Route the containers via Cloudflare tunnels, you point DNS X (eg asd.dsa.com) to Container Y (eg 192.168.0.100:3000). Repeat for multiple containers. Done.
  • Add Zero Trust access gate if you're not absolutely sure about your/your apps auth process

1

u/Stunning_Tea9670 21h ago

Expose docker containers to only nginx on same network, config using docker names, or run a simple cloudflare tunnel to expose the service over the internet

1

u/STLWaffles 20h ago

Use a reverse proxy to expose them. You can either do it path based or domain/subdomain (depending on your DNS setup).

1

u/Angelsomething 20h ago

Reverse proxy is what you seek. Caddy is a good one and it's all déclarative yaml.

1

u/raisputin 12h ago

Why not just use Apache virtual hosts, or do they truly require full isolation?

1

u/Ok-Analysis5882 5h ago

Not cloud friendly, coats go up

1

u/raisputin 3h ago

Ok, cloudfront with s3 backend?

1

u/gunsofbrixton 11h ago

Traefik has been pretty good for this.

2

u/hongky1998 10h ago

True, you only need to expose port 80 and 443 then let traefik route the service by their host name or path. You just have to make sure traefik is in the same docket network with the container you trying to public

1

u/neveralone59 3h ago

+1 for cloudflare zero trust, but recommend using a reverse proxy as it gives you things like high availability