Skip to main content
PostQueen does not handle certificates itself. A reverse proxy in front of it accepts HTTPS on port 443, holds the certificate, and passes each request to PostQueen on a local port. This page sets one up with Caddy, which gets and renews the certificate on its own, and then tells PostQueen its public address.

Why HTTPS is required

  • Sign-in. The login cookie is marked Secure, so the browser sends it only over HTTPS. Over plain HTTP, signing in looks like it works and the next page is the login page again.
  • Connecting channels. Most networks accept only an https redirect address. On a plain http address, PostQueen sends Slack, TikTok, TikTok Business, Threads, Instagram (Standalone) and VK through the public redirectmeto.com service to satisfy them. That helper is for local testing, and it drops out as soon as your address is https.
NOT_SECURED=true removes the cookie requirement for a local try. On a server other people reach, it sends session cookies in the clear: do not set it there.

Which port the proxy sends to

Inside the container PostQueen always listens on 5000. Compose publishes that as 4007 on the host.

Set up Caddy

Before you start, the domain’s A record has to point at the server (dig +short postqueen.example.com prints its IP), and ports 80 and 443 have to be open. Caddy uses port 80 to prove it controls the domain.
1

Install Caddy

On Ubuntu or Debian, from Caddy’s own package repository:
Other systems are on Caddy’s install page. The package runs Caddy as a systemd service.
2

Write the Caddyfile

Replace everything in /etc/caddy/Caddyfile with:
/etc/caddy/Caddyfile
That is the whole configuration. Caddy sees a public name and requests a certificate for it. It sets the forwarding headers, does not cap upload size and streams responses as they arrive, so PostQueen needs nothing more.
3

Reload Caddy

Wait for a line with certificate obtained successfully, then press Ctrl+C.
On a private network where Let’s Encrypt cannot reach the name, add tls internal inside the block. Caddy then issues its own certificate, which browsers trust only after you install Caddy’s local authority on each device: see Caddy’s local HTTPS. Already run another proxy? Nginx and Traefik have their own pages, and Split deployments covers the paths any proxy must route when the frontend and backend run apart.

Tell PostQueen its address

The proxy alone is not enough. PostQueen builds its allowed origins, email links and every network’s redirect address from these values. Put the public address in the .env file next to docker-compose.yaml. The shipped Compose file reads that file and fills these three values into the postqueen service, so there is no YAML to edit:
Keep /api on the last one, and leave off trailing slashes. Leave BACKEND_INTERNAL_URL as it ships, http://localhost:3000: it is the path from the web app to the API inside the container, not a public address. Three rules cover most problems:
  • No trailing slashes. The backend logs should not end with / at startup for each one.
  • FRONTEND_URL is exactly the browser’s address, with https://, the same host and no path. The backend accepts browser requests only from FRONTEND_URL and MAIN_URL.
  • NEXT_PUBLIC_BACKEND_URL keeps /api. Inside the container, nginx sends everything under /api/ to the backend.
Apply it: Recreate the app container so it reads the new values. A plain docker restart or docker compose restart keeps the old container and its old environment. In the folder that holds docker-compose.yaml, recreate only the postqueen service:
The database, Redis and Temporal keep running. Avoid docker compose down for a settings change: on a host that runs other Compose services in the same project, it stops them too. On Kubernetes, see Kubernetes with Helm.

Check it

From your own computer:
HTTP/2 200, then a sign-in in the browser that holds on the next page.

If it does not work

The name does not resolve to this server yet, or port 80 is blocked. sudo journalctl -u caddy -n 50 usually names the reason.
Caddy runs, PostQueen does not answer. On the server, curl -I http://localhost:4007 tells you whether it is up, and docker compose logs postqueen why not.
FRONTEND_URL does not match the address in the browser, character for character. Fix the value and recreate the container.
Caddy sets no size limit, so look at what else is in the path. A CDN or another proxy in front of Caddy often caps the request body, for example at 100 MB. PostQueen itself accepts files up to 1 GB each.

Next steps

Channel developer apps

Each network’s app, variables and redirect URI.

Email

Resend or SMTP, so password resets and invitations arrive.

Uploads and storage

Media on the local disk, or in Cloudflare R2.

Backups and upgrades

The volumes to back up, and how to move to a newer image.
Last modified on September 23, 2026