Configure a production deployment
Set public origins, independent secrets, object storage, email, domain routing, proxy trust, and operator access for self-hosted Nibleaf.
- 3 min read
- Updated Aug 22, 2026
Copy .env.production.example from the release you are deploying. It is the
authoritative setting template. Pin an image, set browser-reachable public
origins, generate independent secrets, and keep internal services private.
Set the public origins
APP_URL is the browser-facing dashboard origin and authentication base for a
normal deployment. Do not add a trailing slash. Keep NIBLEAF_BIND=127.0.0.1
when a reverse proxy runs on the same host.
SITE_BASE_DOMAIN enables project hosts such as
project.docs.example.com; it needs wildcard DNS and wildcard TLS.
CUSTOM_DOMAIN_CNAME_TARGET is a hostname you operate that customer domains
can target. Leave optional domain features empty until their DNS and TLS path is
ready.
Generate independent secrets
Generate secrets on the target host:
openssl rand -hex 32
openssl rand -base64 24
Use separate random values for authentication, the internal API, Postgres, and object storage. Do not copy values from an example, development deployment, support request, or another environment. The production entrypoint rejects known demo authentication secrets.
Two-way Git requires an exactly 32-byte base64 encryption key and a separate API/worker secret of at least 32 characters:
openssl rand -base64 32
openssl rand -hex 32
Deploy GIT_CREDENTIAL_ENCRYPTION_KEY and GIT_WORKER_SECRET to the services
listed by the release Compose file. Read the Git key rotation boundary
before enabling a production repository connection.
Configure object storage
The server uses STORAGE_ENDPOINT for internal requests. The writer's browser
uses presigned URLs based on STORAGE_PUBLIC_ENDPOINT, so that origin must be
publicly reachable and must match how requests are signed. STORAGE_PUBLIC_URL
is the base used when published pages reference assets.
For bundled maxio, route the public storage hostname to maxio port 9000 through TLS. For R2, S3, Backblaze B2, or another compatible provider, make the endpoint, region, path-style behavior, credentials, bucket, CORS origins, and public URL agree. Upload and open a real image before inviting writers.
Configure sign-up and mail
Production requires email verification by default. Set a verified EMAIL_FROM
and configure Postmark or SMTP_URL. Send a verification and password-reset
message to an external mailbox; a container health check cannot prove delivery.
After creating the intended owners, set DISABLE_SIGNUP=true if registration
should close. Existing accounts can still sign in.
Preserve the client address behind a proxy
Forward the original host and HTTPS scheme. Configure TRUSTED_PROXY_HOPS only
after capturing the real public forwarding chain and configuring the ingress to
discard untrusted forwarded headers. A value that is too high lets clients
influence the address used for rate limiting; a value that is too low groups
traffic under the proxy.
Protect operator surfaces
Only the app binds a host port in the production Compose file. Keep Postgres, Dragonfly, the API, the worker, maxio administration, and the optional admin app on the Docker network, loopback, or a private VPN.
Set both WORKBENCH_USER and WORKBENCH_PASS even when worker port 4312 is not
published. Use an SSH tunnel when an operator needs the jobs dashboard.
Review the complete environment-variable reference, then run the production checklist. If the site will be private, complete the reader-access recovery procedure before sending invitations.