Skip to content

Environment variables

Every KALENDEE_* environment variable, the HOCON key it overrides, its default, and the compose-only variables consumed by docker-compose.yml.

Kalendee is HOCON-file-first. Environment variables are not a separate configuration system: they are ${?VAR} substitutions resolved inside whichever HOCON file Ktor loads. See Configuration for the file precedence and the HOCON syntax.

How environment variables work

Every server variable below appears in the baseline config as an optional substitution:

app {
    baseUrl = ""
    baseUrl = ${?KALENDEE_PUBLIC_URL}
    baseUrl = ${?KALENDEE_BASE_URL}
}

Consequences worth internalizing:

  • A variable only applies if the loaded HOCON file contains its ${?VAR} line. The baked /app/application.conf and both example files contain all of them, so this normally just works. A hand-written minimal config that omits a key cannot be set from the environment.
  • Environment variables win over the literal values in that same file. In the snippet above, KALENDEE_PUBLIC_URL replaces "".
  • The file choice still wins at a higher level. -config=/path beats KALENDEE_CONFIG beats /config/application.conf beats /app/application.conf beats the jar. Environment variables never change which file is read.
  • Compose-only variables are different. Variables such as KALENDEE_HOST_PORT, KALENDEE_IMAGE_TAG, and POSTGRES_* are consumed by docker-compose.yml itself and are never seen by the server process.

Recommend HOCON for non-secrets, env for secrets

The repository’s own examples follow this split, and it is the recommended practice:

Setting kindWhere it belongsWhy
Non-secret tuning (URLs, ports, policies, timeouts)application.conf mounted at /config/application.confVersionable, diffable, documented inline, restartable without touching compose secrets.
Secrets (DB password, admin password, OAuth secrets, token keys, SMTP/S3/Mailgun credentials, Worker tokens).env (compose) or the process environmentKeeps secrets out of config files that may be committed or shared.

Both surfaces end up in the same key space, so a setting can move between them without changing behavior. The examples deliberately keep the ${?VAR} lines in the config file so either surface works.

Server variables

All of the following are substitutions inside the loaded HOCON. The “HOCON key” column is the key the variable overrides.

Ktor / bind

VariableHOCON keyDefaultNotes
KALENDEE_HTTP_PORTktor.deployment.port8080Port bound inside the container. Use KALENDEE_HOST_PORT to change the host-side mapping instead.
KALENDEE_HTTP_HOSTktor.deployment.host0.0.0.0Interface to bind.

Database

VariableHOCON keyDefaultNotes
KALENDEE_DATABASE_URLdatabase.urljdbc:postgresql://127.0.0.1:5432/kalendeeJDBC URL. May embed user:password@.
KALENDEE_DATABASE_USERdatabase.userkalendeeDatabase role.
KALENDEE_DATABASE_PASSWORDdatabase.passwordrequiredNo default. Empty is allowed, but the variable/value must be present.

Authentication and sessions

VariableHOCON keyDefaultNotes
KALENDEE_AUTH_REGISTRATIONauth.registrationfirst-userfirst-user, open, or closed.
KALENDEE_AUTH_SESSION_DAYSauth.sessionDays30Session lifetime in days.
KALENDEE_SESSION_COOKIE_NAMEauth.cookieNamekalendee_sessionSession cookie name.
KALENDEE_COOKIE_SECUREauth.cookieSecuresecure outside developmentSet false for plain-HTTP deployments.
KALENDEE_ADMIN_USERNAMEauth.adminUsernameadminFirst-admin username.
KALENDEE_ADMIN_PASSWORDauth.adminPasswordunsetSeeds the admin on startup; blank disables seeding.
KALENDEE_SUPERADMIN_USERNAMEauth.superadminUsernameunsetUsername granted superadmin rights.
KALENDEE_AUTH_EMAIL_VERIFICATIONauth.emailVerificationoptionaloptional, soft, or required.
KALENDEE_AUTH_EMAIL_VERIFICATION_TTL_HOURSauth.emailVerificationTtlHours24Verification-link lifetime.
KALENDEE_AUTH_OAUTH_REGISTRATIONauth.oauthRegistrationfalseAllow OAuth sign-ups (still gated by auth.registration).
KALENDEE_ARGON2_MEMORY_KIBauth.argon2.memoryKib19456Argon2id memory cost (KiB).
KALENDEE_ARGON2_ITERATIONSauth.argon2.iterations2Argon2id iterations.
KALENDEE_ARGON2_PARALLELISMauth.argon2.parallelism1Argon2id parallelism.

Application

VariableHOCON keyDefaultNotes
KALENDEE_PUBLIC_URLapp.baseUrl""Preferred canonical URL.
KALENDEE_BASE_URLapp.baseUrl""Deprecated alias. If set together with KALENDEE_PUBLIC_URL, this later substitution wins, so set only one.
KALENDEE_DEVELOPMENTapp.developmentfalsePretty error page and dev baseUrl default.
KALENDEE_PUBLIC_ACCESSapp.publicAccesspublicpublic or signed_in.
KALENDEE_SEED_DEMOapp.seedDemofalseSeed demo data. Never enable in production.
KALENDEE_DEMO_PASSWORDapp.demoPassworddemoDemo-user password.
KALENDEE_DEMO_TIMEZONEapp.demoTimezoneEurope/BerlinDemo-data time zone.

OAuth / external calendars

VariableHOCON keyDefaultNotes
KALENDEE_DISCORD_CLIENT_IDoauth.discord.clientIdunsetBlank disables Discord.
KALENDEE_DISCORD_CLIENT_SECREToauth.discord.clientSecretunsetDiscord client secret.
KALENDEE_DISCORD_BOT_TOKENoauth.discord.botTokenunsetBot token for guild scheduled events.
KALENDEE_SECRET_KEYoauth.secretKeyunsetBase64 token-vault key, version 1. Required for connections.
KALENDEE_SECRET_KEYSoauth.secretKeysunsetJSON rotation map, e.g. {"1":"<base64>","2":"<base64>"}.

Mail

VariableHOCON keyDefaultNotes
KALENDEE_MAIL_ENABLEDmail.enabledfalseAuto-detection input.
KALENDEE_MAIL_PROVIDERmail.provider"" (auto)smtp, cloudflare, mailgun, or log.
KALENDEE_SMTP_HOSTmail.host""SMTP host.
KALENDEE_SMTP_PORTmail.port587SMTP port.
KALENDEE_SMTP_USERmail.username""SMTP username; blank disables auth.
KALENDEE_SMTP_PASSWORDmail.password""SMTP password.
KALENDEE_MAIL_FROMmail.fromKalendee <no-reply@localhost>From header.
KALENDEE_SMTP_STARTTLSmail.startTlstrueSTARTTLS.
KALENDEE_MAIL_CLOUDFLARE_ENDPOINTmail.cloudflare.endpoint""Mailer Worker base URL.
KALENDEE_MAIL_CLOUDFLARE_TOKENmail.cloudflare.token""Bearer token; matches the Worker’s MAILER_TOKEN.
KALENDEE_MAIL_CLOUDFLARE_TIMEOUT_SECONDSmail.cloudflare.timeoutSeconds10Worker request timeout.
KALENDEE_MAILGUN_API_KEYmail.mailgun.apiKeyunsetMailgun private API key.
KALENDEE_MAILGUN_DOMAINmail.mailgun.domainunsetVerified Mailgun sending domain.
KALENDEE_MAILGUN_REGIONmail.mailgun.regionusus or eu; selects the API host.
KALENDEE_MAILGUN_BASE_URLmail.mailgun.baseUrl""Optional API base URL override.
KALENDEE_MAILGUN_TIMEOUT_SECONDSmail.mailgun.timeoutSeconds10Mailgun request timeout.

Storage

VariableHOCON keyDefaultNotes
KALENDEE_AVATAR_DIRstorage.localDirbuild/avatars (/data/avatars in the image)Local object directory.
KALENDEE_STORAGE_PUBLIC_URLstorage.publicBaseUrl""Public object base URL for redirect-based serving.
KALENDEE_S3_ENABLEDstorage.s3.enabledfalseEnable the S3-compatible backend.
KALENDEE_S3_ENDPOINTstorage.s3.endpoint""S3 endpoint (R2: https://<account>.r2.cloudflarestorage.com).
KALENDEE_S3_REGIONstorage.s3.regionus-east-1Region; auto for R2.
KALENDEE_S3_BUCKETstorage.s3.bucket""Bucket name.
KALENDEE_S3_ACCESS_KEYstorage.s3.accessKeyunsetAccess key id.
KALENDEE_S3_SECRET_KEYstorage.s3.secretKeyunsetSecret access key.
KALENDEE_S3_PATH_STYLEstorage.s3.pathStyletruePath-style addressing; required by R2.

Keel web pack

VariableHOCON keyDefaultNotes
KALENDEE_KEEL_PACKkeel.packDirunsetExploded pack directory or .feb file. Leave unset in production.

Compose-only variables

These are read by docker-compose.yml / docker-compose.dev.yml, not by the server. They configure the container runtime, the port mapping, and the PostgreSQL service.

VariableUsed byDefaultNotes
KALENDEE_CONFIGentrypoint / dev compose/config/application.confPath to the HOCON file the container loads. See precedence above.
KALENDEE_HOST_PORTcompose port mapping8080Host-side port published to container port 8080.
KALENDEE_IMAGE_TAGcompose image:latestTag of docker.yuri.capital/kolektiv/kalendee. Pin a release for reproducibility.
KALENDEE_UIDdev compose user:1000Host UID the dev container runs as (Linux only; remove user: on Docker Desktop).
KALENDEE_GIDdev compose user:1000Host GID for the dev container.
POSTGRES_DBpostgres servicekalendeeDatabase bootstrapped in the postgres container. Match KALENDEE_DATABASE_URL.
POSTGRES_USERpostgres servicekalendeePostgreSQL role bootstrapped. Match KALENDEE_DATABASE_USER.
POSTGRES_PASSWORDpostgres servicerequired in .envComposes .env.example sets this equal to KALENDEE_DATABASE_PASSWORD; both must match.
JAVA_OPTSentrypointunsetExtra JVM flags, e.g. -Xmx512m. Word-split deliberately by the entrypoint.

POSTGRES_PASSWORD is required by docker-compose.yml (${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}), and the server’s KALENDEE_DATABASE_PASSWORD must be the same value.

Minimal production .env

The secrets the examples expect to live in .env:

POSTGRES_PASSWORD=change-me-strong-database-password
KALENDEE_DATABASE_PASSWORD=change-me-strong-database-password
KALENDEE_ADMIN_PASSWORD=change-me-strong-admin-password
KALENDEE_SECRET_KEY=
# KALENDEE_MAIL_CLOUDFLARE_TOKEN=
# KALENDEE_MAILGUN_API_KEY=
# KALENDEE_S3_ACCESS_KEY=
# KALENDEE_S3_SECRET_KEY=

Generate the token-vault key with openssl rand -base64 32. Leave KALENDEE_SECRET_KEY empty until you need external-calendar connections.

Edit this page on GitHub