Every FIKADESK_* variable the api parses, grouped, with defaults and what unset means.
The api reads its configuration from environment variables, parsed by a Zod schema in apps/api/src/env.ts and the module schemas it composes. An invalid value fails the boot with the offending field name, so a typo never survives to the first request.
The unset-means-off pattern
Many features are gated on an optional variable. Compose writes an unset variable through as an empty string, and the schemas read empty as absent, so the legal states are the ones a deployment without that feature runs in. An empty FIKADESK_EMAIL_DOMAIN means no email channel, not an email channel that breaks. An empty FIKADESK_LLM_AGENT_MODEL means the agent sink is absent, so no visitor message reaches a model. Features that are still being wired in follow the same shape: the Meta adapter is live-pending, so all four META_* variables default to absent and no Meta channel exists until they are set.
Some variables are not optional, because a deployment cannot be ambiguous about them. DATABASE_URL, FIKADESK_AUTH_SECRET, FIKADESK_PUBLIC_URL and FIKADESK_REALTIME_SECRET are required, and a boot without them names the field that is missing.
Core
Variable
Default
What it does
FIKADESK_ROLE
api
Which process this image runs. api, worker, scheduler, ingest or migrate. The one-shot migration job uses migrate; no long-running service carries it.
PORT
3000
The port the api listens on.
FIKADESK_HOST
0.0.0.0 in production, 127.0.0.1 otherwise
The listen address. Only a production container sits behind Caddy, so dev and e2e runs stay off the network.
DATABASE_URL
required
The Postgres connection string the api and worker use, through PgBouncer.
DATABASE_URL_DIRECT
DATABASE_URL
A direct connection the outbox drainer uses. It holds a session advisory lock and a LISTEN, which a transaction pooler cannot carry across statements.
LOG_LEVEL
info
One of fatal, error, warn, info, debug, trace.
FIKADESK_TRUSTED_PROXIES
empty
Comma-separated addresses or CIDR ranges whose forwarding headers the api believes. Empty means a direct deployment reads the socket address and nothing else.
FIKADESK_REALTIME_SECRET
required, 32+ characters
Signs the realtime subscription token, which Centrifugo verifies with the same key. Its own secret on purpose: the realtime server must not be able to forge a session.
FIKADESK_REALTIME_URL
http://127.0.0.1:8000
Where the api reaches Centrifugo. The browser never does; the api bridges the stream.
Auth
Variable
Default
What it does
FIKADESK_AUTH_SECRET
required, 32+ characters
Signs sessions and tokens. Generate it with openssl rand -hex 32. Changing it signs everyone out.
FIKADESK_PUBLIC_URL
required
The absolute URL people open. Cookies, email links and the passkey relying party come from it, and it is the origin the api accepts cookie-bearing requests from.
FIKADESK_TRUSTED_ORIGINS
the four development localhost origins
Extra absolute origins allowed to call the api with a cookie, comma separated. Unset is not the same as empty: unset falls back to the development origins, which a deployment must not carry. Set it empty to trust nothing.
FIKADESK_ALLOW_SIGNUP
false
When false, sign-up is invitation-only and workspace creation is limited to instance administrators.
FIKADESK_SETUP_TOKEN
unset
A fixed one-time setup token. Unset, the api prints one while the users table is empty.
FIKADESK_EE_PATH
unset
Path the api loads enterprise extensions from. The OSS image builds with ee/ deleted, so nothing needs this.
Microsoft social sign-in, same both-or-neither rule.
Channels and storage
Variable
Default
What it does
FIKADESK_CHANNELS_SECRET
unset
32 hex bytes that seal channel provider credentials at rest with AES-256-GCM. Unset, the api cannot create an api channel.
FIKADESK_MEDIA_URL
http://localhost:3000/media
The origin attachments upload to and serve from, their own origin so a customer file never runs on the inbox session. Picking it later breaks every URL already embedded in a conversation.
FIKADESK_MESSENGER_ORIGIN
unset
Where the messenger frame is served when that is not the api's own origin. Production serves both from one host and needs none of this.
FIKADESK_STORAGE_ROOT
.storage
Filesystem path the object store keeps files under when no bucket is configured.
FIKADESK_STORAGE_BUCKET
uploads
The object store bucket name.
FIKADESK_IMPORT_MAX_BYTES
2147483648 (2 GiB)
The largest import archive the resumable upload accepts.
FIKADESK_IMPORT_SECRET
unset
32 hex bytes that seal hosted-source importer tokens at rest. Unset, the CSV importer still works but no hosted source can be created or run.
FIKADESK_OUTBOX_SHARDS
1
How many shards the outbox splits into, when more than one drainer runs.
FIKADESK_OUTBOX_SHARD
0
Which shard this drainer owns. Must be below FIKADESK_OUTBOX_SHARDS.
FIKADESK_CAMPAIGN_RATE_PER_SECOND
10
The campaign fan-out's send ceiling in messages per second. A workspace sending to a large audience never fires faster than this.
Email
Variable
Default
What it does
FIKADESK_EMAIL_TRANSPORT
smtp
Which inbound transport this deployment runs. An unknown name stops the process at boot rather than at the first send.
FIKADESK_EMAIL_DOMAIN
unset
The domain this instance owns. Every workspace sends and receives at <email key>@<domain> with no DNS work. Unset, together with the secret, is the no-email-channel state.
FIKADESK_EMAIL_SECRET
unset
32 hex bytes. Encrypts the DKIM private keys and signs the reply token.
FIKADESK_EMAIL_SMTP_URL
unset
Outbound submission, smtps://user:pass@host:465. Without it the channel receives and never sends.
FIKADESK_EMAIL_SMTP_LISTEN
127.0.0.1:2525
Where the SMTP receiver listens. It is not an MX; point the box's own MTA at it.
FIKADESK_EMAIL_SMTP_ALLOW
127.0.0.1/32,::1/128
The peers the receiver accepts, addresses or CIDR ranges. Anything else is refused at connect, before a body streams.
FIKADESK_EMAIL_SPF_INCLUDE
unset
The token the domain wizard asks a customer to add inside their existing v=spf1 record. Unset, it asks for the egress addresses instead.
FIKADESK_EMAIL_EGRESS_IPS
empty
The addresses this instance sends from, used for the SPF record when there is no include token.
FIKADESK_EMAIL_BOUNCE_HOST
unset
The CNAME target of the return-path record a customer publishes, so bounces come back here.
FIKADESK_EMAIL_AUTHSERV_ID
unset
The authserv-id your MTA stamps on the Authentication-Results header it writes. Only that header is believed; unset means no inbound verdict is believed.
FIKADESK_EMAIL_MAX_BYTES
31457280 (30 MB)
Refused at the envelope before a body is read.
FIKADESK_EMAIL_OUTBOUND
smtp
The outbound transport. smtp is a relay you supply; postmark and ses send over each provider's HTTP API.
POSTMARK_SERVER_TOKEN
unset
Postmark outbound credential. Unset, the postmark transport refuses to send.
POSTMARK_MESSAGE_STREAM
unset
The Postmark message stream the transport sends on.
POSTMARK_WEBHOOK_TOKEN
unset
A header token the Postmark delivery webhook must carry; set it or the basic-auth pair below.
POSTMARK_WEBHOOK_USER / POSTMARK_WEBHOOK_PASS
unset
Basic-auth credentials for the Postmark delivery webhook.
SES_ACCESS_KEY / SES_SECRET_KEY / SES_REGION
unset
SES outbound credentials. Unset, the ses transport refuses to send.
AI agent
Variable
Default
What it does
FIKADESK_LLM_AGENT_MODEL
unset
The model that answers visitors, written as provider:model. Unset, or with no embedding endpoint, the agent sink is absent and no visitor message reaches a model.
FIKADESK_LLM_CLASSIFIER_MODEL
unset
The classifier tier's model. Falls back to the agent model.
FIKADESK_LLM_ESCALATION_MODEL
unset
The escalation tier's model. Falls back to the agent model.
FIKADESK_LLM_ANTHROPIC_API_KEY
unset
Credential for anthropic: models.
FIKADESK_LLM_OPENAI_API_KEY
unset
Credential for openai: models.
FIKADESK_LLM_OPENAI_COMPATIBLE_API_KEY
unset
Credential for an OpenAI-compatible endpoint.
FIKADESK_LLM_OPENAI_COMPATIBLE_BASE_URL
unset
Base URL of the OpenAI-compatible endpoint.
FIKADESK_LLM_EMBEDDING_MODEL
unset
The embedding model, always served by the OpenAI-compatible endpoint, so a plain model name the /embeddings call sends as-is.
Meta
Variable
Default
What it does
META_APP_ID
unset
Names the Meta app the webhook subscription belongs to.
META_APP_SECRET
unset
Signs every Meta webhook. Unset, or with no verify token, no Meta channel exists at all.
META_VERIFY_TOKEN
unset
The shared handshake secret that answers Meta's subscription check.
META_WHATSAPP_TOKEN
unset
Sends WhatsApp messages and downloads media. Without it, WhatsApp outbound is off.
Billing
Billing is optional and env-gated. With no provider configured the billing routes stay unmounted and the meter job never registers.
Variable
Default
What it does
FIKADESK_BILLING_PROVIDER
unset
stripe or paddle. Unset, the image infers it from whichever key is present. Both providers configured without naming one fails the boot.
These variables are read by deploy/compose/compose.yml, not by the api's schema. They shape the stack itself. The full template with comments lives at deploy/compose/.env.example.
Variable
Default
What it does
FIKADESK_IMAGE
ghcr.io/fikadesk/fikadesk:latest
The image to run. Pin a release tag; upgrading is changing this line.
POSTGRES_PASSWORD / FIKADESK_APP_PASSWORD
empty
Owner and app role passwords. Generate each with openssl rand -hex 24.
FIKADESK_BIND
127.0.0.1
The address Caddy publishes on. 0.0.0.0 opens it to the world.
FIKADESK_HTTP_PORT / FIKADESK_HTTPS_PORT
80 / 443
The host ports Caddy publishes, kept separate so the stack can share a box.
FIKADESK_DOMAIN
empty
The public hostname. Empty serves plain HTTP; set it and point DNS here and Caddy fetches a certificate.
FIKADESK_UPSTREAM_PROXIES
empty
Addresses and CIDR ranges of a proxy or CDN in front of Caddy, space separated. Empty means Caddy believes no forwarding header.
The internal bridge Caddy, the api and the worker share. Change all three together, only if the subnet collides.
FIKADESK_SMTP_PORT
2525
The host port the ingest container publishes SMTP on.
Backups
Backups run pgBackRest. With no backup variables set, WAL archiving and the scheduled backups go to a local pgbackrest volume you copy off the box yourself. Set all five PGBACKREST_REPO1_S3_* variables to point the repository at S3-compatible storage, or all four R2_* variables to add a second repository on Cloudflare R2. The backup container refuses to start unless every variable in a set is present.