Run in production
Serve Sluby on your domain over HTTPS with the production override and Caddy.
This page puts your Sluby server on a public domain over HTTPS. The production override, docker-compose.prod.yml, adds Caddy in front of the stack, which gets and renews a TLS certificate for your domain. It also turns on the production checks, requires the secrets, and publishes nothing but ports 80 and 443.
Install the stack first with Install Sluby and connect it to Sia with Connect Sia storage. This page assumes the Sluby folder and its .env.
Before you start
- A domain or subdomain, for example
sluby.example.com, whose A or AAAA record points at the server. - Ports 80 and 443 of the server reachable from the internet. The override publishes
80/tcp,443/tcpand443/udpfor Caddy.
Deploy
Generate the secrets
Generate one value for the database password and one for the session secret:
openssl rand -hex 32
openssl rand -base64 32Use the hex value for the database password. Compose puts the password inside the database URL, and a / (which base64 output can contain) makes that URL invalid, so the backend refuses to start.
Set the production lines in .env
DOMAIN=sluby.example.com
ACME_EMAIL=you@example.com
POSTGRES_PASSWORD=<the hex value>
SESSION_SECRET=<the base64 value>
SIA_APP_ID=<from connect-indexer.mjs>
SIA_APP_KEY=<from connect-indexer.mjs>Compose refuses to start the production stack while any of these six is empty. What each one does: DOMAIN, ACME_EMAIL, POSTGRES_PASSWORD, SESSION_SECRET, SIA_APP_ID, SIA_APP_KEY.
Start the stack
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --buildUse both -f files in every later docker compose command on this server, so Compose keeps the production settings.
Create the first admin
docker compose -f docker-compose.yml -f docker-compose.prod.yml logs backend | grep "setup code"Open https://sluby.example.com/studio, click Set up the first admin and enter the code. More on the setup code: Create the first admin.
What the override changes
| Setting | Base stack | With docker-compose.prod.yml |
|---|---|---|
| Published ports | web on 127.0.0.1:8080 | caddy on 80, 443 and 443/udp; nothing else |
| TLS | none | Caddy, for DOMAIN |
NODE_ENV | development | production |
PUBLIC_URL | http://localhost:8080 | https:// plus DOMAIN |
CORS_ORIGIN | empty | https:// plus DOMAIN |
AUTH_DISABLED | from .env | always false |
Required in .env | nothing | DOMAIN, ACME_EMAIL, POSTGRES_PASSWORD, SESSION_SECRET, SIA_APP_ID, SIA_APP_KEY |
The override sets NODE_ENV, PUBLIC_URL, CORS_ORIGIN and AUTH_DISABLED itself, so values for them in .env have no effect on the production stack.
With NODE_ENV=production the backend runs its startup checks and writes JSON logs. What it checks: NODE_ENV.
Caddy compresses responses with zstd or gzip, sends Strict-Transport-Security: max-age=31536000, removes the Server header and passes every request to the web service.
Move a local install to production
The production stack uses the same volumes as the local stack, so your videos and accounts stay. Two settings behave differently when the data already exists:
-
POSTGRES_PASSWORD: thepostgresimage sets the password only when it creates the database on an empty volume. A database created by the local stack keeps the password it started with:sluby, unless you setPOSTGRES_PASSWORDbefore its first start. While the local stack is still running, change the password inside the database to the hex value you generated:docker compose exec postgres psql -U sluby -d sluby -c "ALTER USER sluby PASSWORD '<the hex value>'"Then set the same value as
POSTGRES_PASSWORDin.env. -
SESSION_SECRET: the local stack ran with a secret it generated and stored in its database. Setting your own secret signs everyone out and makes signed links handed out earlier stop working (SESSION_SECRET). Accounts stay in the database, so an admin who could sign in before can sign in again. When no admin can sign in, the backend logs a new setup code at start: Create the first admin.
Run behind your own proxy
If the server already has a reverse proxy that terminates TLS, run the base stack without the override and point your proxy at the web service on 127.0.0.1:8080 (or the port in WEB_PORT).
Set these lines in .env:
NODE_ENV=production
PUBLIC_URL=https://sluby.example.com
CORS_ORIGIN=https://sluby.example.com
POSTGRES_PASSWORD=<openssl rand -hex 32>
SESSION_SECRET=<openssl rand -base64 32>Start the base stack:
docker compose up -d --buildConfigure your proxy to:
- pass the original
Hostheader,X-Forwarded-ForandX-Forwarded-Proto: https. The web service takes the client address fromX-Forwarded-Forwhen the connection comes from a loopback or private address, and passes the scheme on to the backend. - accept request bodies of at least 10 MiB. The Studio uploads in 8 MiB chunks and
@sluby/sdkin 10 MiB chunks by default (chunkSize). - send
Strict-Transport-Securityif you want HSTS. The web service does not send it.
Related
- Configuration reference
- Set up sign-in, including GitHub sign-in for your domain
- Monitor and maintain