Configuration

Reference for every environment variable, optional integrations, custom branding, and how to upgrade between releases.

Required variables

These must be set for the stack to start. The bundled docker-compose.yml reads them from the .env file at the repo root.

DOMAINrequiredThe public hostname this instance serves on. Used by Traefik for routing and Let's Encrypt for cert issuance. No protocol — e.g. notipo.example.com.
ACME_EMAILrequiredEmail address Let's Encrypt sends cert-expiry notifications to.
DB_PASSWORDrequiredPostgres password for the bundled notipo user. Generated once with openssl rand -hex 24 is plenty.
DATABASE_URLrequiredPostgres connection string. With the bundled compose stack: postgresql://notipo:${DB_PASSWORD}@postgres:5432/notipo.
ENCRYPTION_KEYrequired64-character hex string used to encrypt every WordPress and Notion credential at rest. Generate once with openssl rand -hex 32 and back it up. Rotating it loses access to all stored credentials.
API_KEYrequiredAdmin API key for the /api/admin/* routes. Long random string.
ALLOW_SIGNUPWhether new accounts can be created via the public signup page. Set to false after creating your account if you want a single-tenant instance.
Default: true

Email (optional)

Notipo only sends transactional email if Resend is configured. Without it, the API skips email entirely and adapts:

  • Signup: new accounts are auto-verified on creation and the user is logged in immediately. No verification link sent.
  • Password reset: the "forgot password" flow returns success but no email is sent — there's no way to reset a password without email. If you lose your password and Resend isn't configured, you have to reset the passwordHash directly in the database.
  • Admin signup notifications, onboarding reminders, trial-expiry warnings: all silently no-op.

If you want any of those features, configure Resend:

RESEND_API_KEYResend API key. Sign up at resend.com; free tier covers most personal use.
RESEND_FROM_EMAILAddress transactional emails are sent from. Domain must be verified in Resend (SPF + DKIM).

Custom branding

Two variables let you rebrand transactional emails without forking the codebase — set them and the welcome / verification / password-reset emails switch from saying "Notipo" to whatever you choose:

BRAND_NAMEDisplay name used in email subject lines and bodies. E.g. "Acme Publish".
Default: Notipo
SUPPORT_EMAILReply-to address shown in transactional emails.
Default: support@notipo.com

Optional integrations

These unlock additional features. The stack works without any of them.

GEMINI_API_KEYEnables AI-generated featured images via Google Gemini's free 2.5 Flash Image model. Without it, featured images use Unsplash photos or category-based fallbacks.
UNSPLASH_ACCESS_KEYUnlocks the Unsplash search backend for featured images. Without it, the featured-image generator falls back to a gradient.
STRIPE_SECRET_KEYEnables paid plans + Stripe Checkout. Leave unset for self-host mode (all features unlocked, no plan limits, new signups land on Pro automatically).
STRIPE_WEBHOOK_SECRETRequired if STRIPE_SECRET_KEY is set. Verifies inbound Stripe webhook signatures.
STRIPE_PRO_PRICE_IDRequired if STRIPE_SECRET_KEY is set. Stripe price ID for the Pro plan.
NOTION_OAUTH_CLIENT_IDEnables Notion OAuth in addition to manual integration tokens. Create a public Notion integration to get the ID and secret.
NOTION_OAUTH_CLIENT_SECRETRequired if NOTION_OAUTH_CLIENT_ID is set.
NOTION_WEBHOOK_SECRETEnables instant Notion webhook delivery (instead of the 5-minute fallback poll). Set to whatever Notion provides during webhook subscription.
GCS_BUCKETGoogle Cloud Storage bucket name for category-image uploads. If unset, uploads fall back to local disk under the uploads/ volume.
ADMIN_NOTIFY_EMAILIf set, Notipo sends you an email whenever a new user signs up. Useful for monitoring a private instance.
POLL_INTERVAL_SECONDSNotion safety-net poll interval. The Notion webhook is the primary trigger; this catches missed events.
Default: 300
LOG_LEVELpino log level. One of trace, debug, info, warn, error.
Default: info

Upgrading

New tagged releases publish multi-arch images to GHCR. To upgrade:

# Pull the latest images (or a specific tag)
docker compose pull

# Recreate containers with new images. Migrations run automatically
# from the API container's entrypoint on startup.
docker compose up -d

For predictable upgrades, pin to a specific minor in docker-compose.yml instead of :latest:

services:
  app:
    image: ghcr.io/kfuras/notipo-api:1.2

  web:
    image: ghcr.io/kfuras/notipo-web:1.2

That gets you patches automatically (1.2.0 → 1.2.1 → 1.2.2) without surprise majors. Bump the minor manually after reading the release notes.

Back up before upgrading
Take a Postgres backup before any minor or major upgrade. docker compose exec postgres pg_dump -U notipo notipo > backup.sqlis the simplest one-liner. Migrations are forward-only — there's no built-in down path.

Backing up your data

Two pieces of state matter:

  • The Postgres database — users, posts, jobs, encrypted credentials. Back it up regularly.
  • Your ENCRYPTION_KEY — without it, the encrypted credentials in the DB are useless. Store it separately from the DB backup so a single-source compromise can't exfiltrate both.
# Database backup
docker compose exec -T postgres pg_dump -U notipo notipo > notipo-backup.sql

# Restore
cat notipo-backup.sql | docker compose exec -T postgres psql -U notipo -d notipo