Updated: 28 September 2026 · Applies to: Ubuntu 24.04, Debian 12, AlmaLinux 9 and Rocky Linux 9
With the nginx-proxy and acme-companion containers, each new container gets its own domain and a free Let's Encrypt certificate automatically.
Install Docker Engine (official repository)
Ubuntu 24.04 / Debian 12:
apt install ca-certificates curl install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc tee /etc/apt/sources.list.d/docker.sources <<EOF Types: deb URIs: https://download.docker.com/linux/ubuntu Suites: $(. /etc/os-release && echo "$VERSION_CODENAME") Components: stable Architectures: $(dpkg --print-architecture) Signed-By: /etc/apt/keyrings/docker.asc EOF apt update apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
On Debian, use https://download.docker.com/linux/debian in both the key URL and URIs.
AlmaLinux 9 / Rocky Linux 9:
dnf install dnf-plugins-core dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin systemctl enable --now docker
Test with docker run hello-world. Compose is available as docker compose.
compose.yaml
services:
proxy:
image: nginxproxy/nginx-proxy
ports: ["80:80", "443:443"]
volumes:
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- /var/run/docker.sock:/tmp/docker.sock:ro
acme:
image: nginxproxy/acme-companion
environment:
DEFAULT_EMAIL: you@example.com
volumes_from: [proxy]
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- acme:/etc/acme.sh
site1:
image: nginx
environment:
VIRTUAL_HOST: site1.example.com
ACME_HOST: site1.example.com
volumes: {certs: {}, html: {}, acme: {}}
Start with docker compose up -d. Add more sites as services with their own VIRTUAL_HOST and ACME_HOST (older examples call it LETSENCRYPT_HOST). Each domain must point to the server.
How it works
nginx-proxy watches Docker. When a container starts with VIRTUAL_HOST, it adds an Nginx site for that domain and forwards traffic to the container. acme-companion sees ACME_HOST, requests a Let's Encrypt certificate, and renews it automatically.
Add another site
docker run -d --name site2 --network <project>_default \ -e VIRTUAL_HOST=site2.example.com -e ACME_HOST=site2.example.com nginx
The container must be on the same Docker network as the proxy (docker network ls shows the Compose network name). If the app listens on a port other than 80, add VIRTUAL_PORT (for example VIRTUAL_PORT=3000).
Common problems
- No certificate: the domain must point to this server and port 80 must be reachable from the internet. Check
docker compose logs acme. - 502 Bad Gateway: the app container is not running, is on another network, or needs
VIRTUAL_PORT.