Tanstack Start
Self-hosting

Environment variables

Every environment variable Feeblo understands — what each one does, which app reads it, and the defaults — grouped by section, sourced from .reference/.env.example and the Effect configs.

Feeblo is configured entirely through environment variables read at startup. This page is the canonical walkthrough: every group, every variable, who reads it, and the footguns. Values come from .reference/.env.example plus the Effect Config calls that actually consume them.

This page is based on .reference/.env.example, apps/server/src/config.ts, packages/auth/src/config.ts, packages/domain/src/auth/config.ts, packages/domain/src/services/s3-config.ts, packages/transactional/src/config.ts, packages/web-shared/src/lib/runtime-public-env.ts, apps/web/astro.config.mjs, and apps/web/wrangler.jsonc.

Which app reads what

Variable groupRead by serverRead by web
DatabaseDATABASE_URL
AuthAUTH_ENCRYPTION_KEY + optional providersTURNSTILE_SITE_KEY (client)
AppAPP_URL, API_URL, APP_ROOT_DOMAIN, NODE_ENVAPP_URL, API_URL, APP_ROOT_DOMAIN, APP_RELEASE
SMTP✅ all SMTP_*
Media✅ all MEDIA_UPLOAD_*

The web app reads a much smaller set: only the App variables plus APP_RELEASE and TURNSTILE_SITE_KEY. Its Astro env schema (in astro.config.mjs) marks API_URL, APP_URL, APP_ROOT_DOMAIN as required, APP_RELEASE and TURNSTILE_SITE_KEY as optional. On Cloudflare, wrangler.jsonc lists API_URL, APP_URL, APP_ROOT_DOMAIN as required secrets.

[[DATABASE]]

The only database variable is DATABASE_URL; see the database page for connection behaviour.

Prop

Type

[[AUTH]]

AUTH_ENCRYPTION_KEY is read in three places — packages/auth/src/config.ts (AuthConfig), packages/domain/src/auth/config.ts (VerificationOtpConfig), and implicit session signing — and always as a required value. Keep it identical across restarts — it signs/encrypts sessions, so changing it invalidates every existing session.

Prop

Type

Optional auth providers (not in .env.example)

AuthConfig (packages/auth/src/config.ts) reads several optional values that the example file doesn't list. They're all read through optionalString / Config.option, so leaving them unset simply disables that feature:

Prop

Type

The web app reads the site (public) half of Turnstile separately:

Prop

Type

[[APP]]

These are the only App variables the shipped docker-compose.yml forwards into both server and web. They're required everywhere Feeblo runs.

Prop

Type

URL vs host, and the port-stripping quirk

APP_URL and API_URL are compared against request origins in apps/server/src/index.ts using new URL(...).hostname. APP_ROOT_DOMAIN, though, is matched against the request hostname after stripping a single :port suffix — and never URL-parsed. Set it to the bare host (feeblo.example.com), not https://…. The dev example's APP_ROOT_DOMAIN=localhost:3001 works because the port is stripped and the NODE_ENV=development branch whitelists localhost.

[[SMTP]]

Transactional email over a single nodemailer SMTP transport. See the email page for what the code actually reads and the variables the example declares but ignores.

Prop

Type

[[MEDIA_UPLOAD]]

S3-compatible object storage for uploads. See the media page for bucket policies, public URL construction, and the R2/MinIO specifics.

Prop

Type

There's one media var not in .env.example: MEDIA_PUBLIC_BASE_URL (optional, string). Set it when the public file URL differs from the S3 API endpoint — see the media page.

Putting it all together

A minimal self-host .env (assuming the compose stack from the Docker guide fully provisions Postgres / Mailpit / MinIO):

# App
APP_URL=https://feeblo.example.com
API_URL=https://api.feeblo.example.com
APP_ROOT_DOMAIN=feeblo.example.com

# Database
DATABASE_URL=postgres://feeblo:change-me@postgres:5432/feeblo

# Auth
AUTH_ENCRYPTION_KEY=generate-a-long-random-string

# SMTP
SMTP_HOST=feeblo-smtp
SMTP_PORT=1025
SMTP_FROM_ADDRESS=noreply@feeblo.example.com

# Media
MEDIA_UPLOAD_REGION=us-east-1
MEDIA_UPLOAD_ENDPOINT=http://feeblo-minio:9002
MEDIA_UPLOAD_ACCESS_KEY_ID=feeblo
MEDIA_UPLOAD_SECRET_ACCESS_KEY=change-me-too
MEDIA_PUBLIC_BUCKET_NAME=feeblo-media-public

On this page