Getting
Step 1 — do not publish the container to the world. Bind it to loopback:
Now nothing reaches it except from the host itself. This one change removes an entire category of problem, which is people finding your app on port 3000 before you finished configuring it. Several templates on this install already ship this way by default — MinIO's S3 API and the Ollama API are both bound to 127.0.0.1 for exactly this reason, because neither has a password of its own worth the name.
Step 2 — DNS first, certificate second. The A record has to resolve and reach port 80 before Let's Encrypt can validate anything. This is the order that trips everybody: people configure the vhost with
Step 3 — the vhost. The minimum that actually works, including the two lines people forget:
The
Step 4 — keep the ACME path unproxied.
If this block is missing or sits below the catch-all
What about Cloudflare? Orange cloud is fine for HTTP and HTTPS. It is not fine for anything that is not HTTP — a voice server, a game server, SMTP on 465 or 587. Those records stay grey.
Panels that manage this write the whole vhost for you when you link a domain to a container, including the WebSocket headers, the ACME location and TLS 1.2/1.3 with the certificate paths already pointing where the ACME client will put them. Worth using if you have it, because the four steps above are exactly the four that get done out of order by hand.
app.example.com to serve a container is four steps, and three of them are usually done in the wrong order.Step 1 — do not publish the container to the world. Bind it to loopback:
Code:
ports:
- "127.0.0.1:3000:3000"
Now nothing reaches it except from the host itself. This one change removes an entire category of problem, which is people finding your app on port 3000 before you finished configuring it. Several templates on this install already ship this way by default — MinIO's S3 API and the Ollama API are both bound to 127.0.0.1 for exactly this reason, because neither has a password of its own worth the name.
Step 2 — DNS first, certificate second. The A record has to resolve and reach port 80 before Let's Encrypt can validate anything. This is the order that trips everybody: people configure the vhost with
ssl_certificate pointing at files that do not exist yet, nginx refuses to reload, and they conclude the proxy is broken.Step 3 — the vhost. The minimum that actually works, including the two lines people forget:
Code:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
# WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
The
Upgrade and Connection headers are what make live logs, terminals, chat and notification streams work. Without them the app loads perfectly and then everything real-time in it fails, which reads like an application bug and is not.X-Forwarded-Proto is the other one. Miss it and apps that build their own URLs — Nextcloud, Chatwoot, anything with a login redirect — will happily redirect your HTTPS visitors to http://, producing a redirect loop that looks like a Cloudflare problem.Step 4 — keep the ACME path unproxied.
Code:
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type text/plain;
try_files $uri =404;
}
If this block is missing or sits below the catch-all
location /, the renewal challenge gets proxied into your container, the container returns 404, and the certificate silently fails to renew 60 days later. That is the classic "it worked for two months" bug.What about Cloudflare? Orange cloud is fine for HTTP and HTTPS. It is not fine for anything that is not HTTP — a voice server, a game server, SMTP on 465 or 587. Those records stay grey.
Panels that manage this write the whole vhost for you when you link a domain to a container, including the WebSocket headers, the ACME location and TLS 1.2/1.3 with the certificate paths already pointing where the ACME client will put them. Worth using if you have it, because the four steps above are exactly the four that get done out of order by hand.