Environment variables
This is the full reference for self-hosted instances. Start with the small .env example in the Docker guide; add other settings only when you need them.
Production startup requires a secret, the Quipthread URL, allowed publisher origins, and a sign-in provider. Email sign-in also requires an SMTP host and sender address.
Required
Section titled “Required”| Variable | Default | Description |
|---|---|---|
JWT_SECRET |
— | Required. Secret key used to sign session tokens. Generate with openssl rand -hex 32. |
Server
Section titled “Server”| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
TCP port the HTTP server listens on. |
BASE_URL |
http://localhost:8080 |
Public origin of Quipthread, such as https://comments.example.com. OAuth callbacks append /auth/github/callback or /auth/google/callback; do not put that path in BASE_URL. |
TRUST_PROXY |
false |
Trust forwarded client IP headers. Set to true behind Caddy or another trusted proxy, and prevent direct access to the app port. |
LOG_FORMAT |
text | Set to json for structured logs. |
LOG_LEVEL |
info | Set to debug for debug logs. |
Database
Section titled “Database”| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
./data/comments.db |
Path to the local SQLite database file. Docker Compose defaults to /data/db.sqlite in its persistent volume. Self-hosted mode rejects remote libsql://, http://, and https:// URLs before opening a driver. |
Auth — OAuth
Section titled “Auth — OAuth”| Variable | Default | Description |
|---|---|---|
GITHUB_CLIENT_ID |
— | GitHub OAuth app client ID. |
GITHUB_CLIENT_SECRET |
— | GitHub OAuth app client secret. |
GOOGLE_CLIENT_ID |
— | Google OAuth client ID. |
GOOGLE_CLIENT_SECRET |
— | Google OAuth client secret. |
See OAuth Setup for how to create these credentials.
Auth — Email
Section titled “Auth — Email”| Variable | Default | Description |
|---|---|---|
EMAIL_AUTH_ENABLED |
false |
Set to true to enable email+password registration and login. |
SMTP_HOST |
— | SMTP server hostname. |
SMTP_PORT |
587 |
SMTP server port. |
SMTP_USER |
— | SMTP authentication username. |
SMTP_PASS |
— | SMTP authentication password. |
SMTP_FROM |
— | Sender address for all outgoing email. |
EMAIL_PROVIDER |
SMTP | Delivery path. Empty uses SMTP; cloudflare sends authentication email and notification digests over the Cloudflare Email Sending REST API; postmark and sendgrid send notification digests over their HTTP APIs while authentication email falls back to SMTP; ses uses SMTP. An unknown value fails at startup. |
EMAIL_REPLY_TO |
— | Optional Reply-To for Cloudflare authentication and notification email. Only EMAIL_PROVIDER=cloudflare sets it; SMTP, Postmark, SendGrid, and SES ignore it. When empty, replies go to SMTP_FROM; no address is hardcoded. |
CLOUDFLARE_API_TOKEN |
— | Cloudflare API token with email sending permission. Required when EMAIL_PROVIDER=cloudflare; distinct from any Turso token. |
CLOUDFLARE_ACCOUNT_ID |
— | Cloudflare account that owns the sending domain. Required when EMAIL_PROVIDER=cloudflare. |
See Email Authentication for details.
The sender selection is compiled into every build, so it is available
self-hosted, but the supported self-hosted configuration is SMTP. The Cloudflare
Email Sending path is the managed-cloud default: when EMAIL_PROVIDER=cloudflare
is set, SMTP_FROM is the sender and its domain must be onboarded to Email
Sending, and SMTP_HOST is not required because delivery is over HTTPS.
| Variable | Default | Description |
|---|---|---|
ALLOWED_ORIGINS |
— | Required in production. Comma-separated publisher origins, such as https://example.com,https://www.example.com. Include scheme and port where needed; paths and wildcards are rejected. BASE_URL is accepted separately for dashboard requests. |
Rate limiting
Section titled “Rate limiting”| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_COMMENTS |
5/10m |
Rate limit for comment submissions. Format: count/duration where duration uses Go time syntax (e.g. 10m, 1h). 5/10m means 5 requests per 10 minutes per IP. |
RATE_LIMIT_AUTH |
10/5m |
Rate limit for auth endpoints. Same format as above. |
Spam filtering
Section titled “Spam filtering”| Variable | Default | Description |
|---|---|---|
SPAM_MAX_LINKS |
3 |
Maximum number of links (hrefs or bare URLs) allowed in a comment before it is auto-rejected as spam. |
Notifications
Section titled “Notifications”Self-hosted builds support SMTP email notifications. Configure the SMTP_* settings above.
| Variable | Default | Description |
|---|---|---|
NOTIFY_BATCH_SIZE |
5 |
Number of pending comments to accumulate before sending a notification. |
NOTIFY_COOLDOWN_HOURS |
24 |
Legacy (inactive). Parsed for compatibility but read nowhere at runtime. Self-hosted dispatch uses 5 minutes; cloud dispatch uses the site interval or a 5-minute default. Setting this variable has no effect. |
NOTIFY_EMAIL_TO |
— | Fallback notification recipient email address, used when the site owner has no email address on their account. |
See Notifications for delivery settings and channel availability.
Bot protection
Section titled “Bot protection”| Variable | Default | Description |
|---|---|---|
TURNSTILE_SITE_KEY |
— | Cloudflare Turnstile site key. Served to the embed widget to render the challenge. |
TURNSTILE_SECRET_KEY |
— | Cloudflare Turnstile secret key. Used for server-side token verification. |
Both variables must be set to enable Turnstile bot protection from the environment. Keys saved on the dashboard’s Account page take priority over these variables, so you can also leave both unset and configure Turnstile there.
Docker backups
Section titled “Docker backups”These variables are read by the Docker entrypoint, not by the Go backend:
| Variable | Default | Description |
|---|---|---|
LITESTREAM_REPLICA_URL |
— | Replica URL. A nonempty value enables restore-on-startup and continuous replication with the packaged Litestream v0.3.14. |
LITESTREAM_ACCESS_KEY_ID |
— | Access key for the replica bucket. |
LITESTREAM_SECRET_ACCESS_KEY |
— | Secret key for the replica bucket. |
See Backups and restore for setup and restore behavior.
Managed cloud settings
Section titled “Managed cloud settings”Self-hosted builds use local SQLite and do not include managed billing or tenant provisioning. Leave CLOUD_MODE unset or false; setting it to true does not convert a self-hosted image into the managed platform. Turso and Stripe credentials are not needed for self-hosting.
How configuration is loaded
Section titled “How configuration is loaded”Process environment variables take priority. The backend then loads ../.env and .env without overwriting values already set. Docker Compose passes .env through env_file. Keep one configuration source per deployment to avoid conflicting values.