What's new
Panelica Community Forum

Welcome to the official Panelica Community Forum — the central hub for server administrators, developers, and hosting professionals. Register a free account today to access technical discussions, product announcements, feature requests, and direct support from the Panelica team. Be part of the growing community shaping the future of server management.

Pointing a domain at a container: reverse proxy, WebSockets and certificate order

admin

Administrator
Staff member
Getting 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.
 
Back
Top