Back to Overview

Environment Variables Reference

What each Tymeslot environment variable does, its default, whether it is required, and an example value.

Luka Breitig, Software Engineer & AI Developer
Luka Breitig

Оновлено 17 вересня 2026

⚙️ Core Application

SECRET_KEY_BASE Required

Secret of at least 64 characters that signs and encrypts sessions. Unless you set DATA_ENCRYPTION_KEY (below), it also derives the key that encrypts integration credentials at rest, so rotating it would make stored credentials undecryptable. Generate one with the command in the example. On Cloudron it is generated automatically.

Default: none  ·  Example: openssl rand -base64 64 | tr -d '\n'

DATA_ENCRYPTION_KEY Optional

Dedicated key for encrypting integration credentials at rest, independent of SECRET_KEY_BASE. Setting it lets you rotate SECRET_KEY_BASE without breaking stored credentials. The value must be Base64 that decodes to at least 32 bytes, and must never change once set. On Cloudron it is generated automatically. See the Data-at-Rest Encryption guide for migration steps.

Default: derived from SECRET_KEY_BASE  ·  Example: openssl rand -base64 48 | tr -d '\n'

PHX_HOST Required

Your domain name without the protocol prefix. It sets the host of every generated link, including the ones in password reset and verification emails, and the origin allowed to open WebSocket connections. On Cloudron it defaults to the app's domain. A development server (mix phx.server) reads it too, falling back to localhost when it is unset.

Default: none  ·  Example: tymeslot.yourdomain.com

PORT Optional

HTTP port the application server listens on. The Railway image sets it to 8080.

Default: 4000  ·  Example: 4000

DEPLOYMENT_TYPE Optional

Deployment variant: cloudron or docker. It selects where database settings are read from, the WebSocket origins, the URL scheme and mail auto-detection. Any value other than cloudron, including unset, means docker, which also covers Railway and bare releases. The official images set it for you.

Default: docker  ·  Example: docker

REGISTRATION_ENABLED Optional

Set to false to stop new users signing up on this instance. Leave it unset to switch it from the admin UI instead, without a restart. While the variable holds a recognised value (here true or false) it takes precedence, and the admin UI shows the setting greyed out. Any other value, such as 1, does not take precedence: the admin UI can still change the setting, and Tymeslot logs a warning at startup naming the variable. The same applies to every variable on this page that the admin UI can manage. If registration is closed and no accounts are left, the admin setup guide's "No accounts left and registration closed" section shows how to get back in.

Default: true  ·  Example: false

PASSWORD_AUTH_ENABLED Optional

Set to false to turn off email and password login. The login form, sign-up page and password reset flow are hidden, so users can only sign in with OAuth. Use it for SSO-only deployments where everyone authenticates through an identity provider. Leave it unset to switch it from the admin UI instead. Accepts true or false; while it holds one of those, it takes precedence.

Default: true  ·  Example: false

LISTEN_IP Optional

IP address the HTTP server binds to. The default, ::, listens on all interfaces over IPv4 and IPv6 on dual-stack hosts. Use 0.0.0.0 on an IPv4-only host.

Default: ::  ·  Example: 0.0.0.0

URL_SCHEME Optional

Scheme used in generated URLs. Keep https in production; with it, links carry no port suffix. Cloudron always uses https and ignores this variable. A development server defaults to http on the listen port instead; setting this to https there tells it the proxy terminates TLS on the standard port, so generated links carry no port suffix.

Default: https  ·  Example: https

WS_ALLOWED_ORIGINS Optional

Comma-separated list of origins allowed to open WebSocket connections, each with its scheme: a bare hostname is rejected. Unset, it allows PHX_HOST over http and https, plus localhost:4000. Setting it replaces that list. Cloudron ignores it.

Default: derived from PHX_HOST  ·  Example: https://tymeslot.yourdomain.com,https://app.example.com

🗄️ Database

DATABASE_URL Optional

Full PostgreSQL connection string. Its host, port, database, user and password take precedence over the discrete variables below. When it names a remote host the Docker container skips its bundled database; a URL pointing at localhost keeps using it.

Default: unset  ·  Example: postgres://user:pass@db.example.com:5432/tymeslot

DATABASE_SSL Optional

TLS for the database connection. true (or verify-full) encrypts and verifies the server certificate and hostname; verify-none encrypts without verifying; false turns TLS off. Unset, an sslmode parameter in DATABASE_URL decides. Managed providers such as RDS, Neon and Supabase generally require TLS.

Default: unset  ·  Example: true

DATABASE_SSL_CACERT_FILE Optional

CA bundle used to verify the database certificate, for providers with a private CA. Read only when TLS verification is on; without it, the system trust store is used. Keep the file on the /app/data volume so it survives upgrades. Tymeslot refuses to start if the file does not exist.

Default: unset  ·  Example: /app/data/rds-ca-bundle.pem

DATABASE_HOST Optional

PostgreSQL server hostname. In the Docker image, any value other than localhost or 127.0.0.1 switches to an external database.

Default: localhost  ·  Example: db.internal

DATABASE_PORT Optional

PostgreSQL server port.

Default: 5432  ·  Example: 5432

POSTGRES_DB Optional

Database name.

Default: tymeslot  ·  Example: tymeslot

POSTGRES_USER Optional

Database user.

Default: tymeslot  ·  Example: tymeslot

POSTGRES_PASSWORD Optional

Database password. The Docker image's bundled database falls back to tymeslot, which is safe because that database is reachable only inside the container. Set a strong one for an external database, and in docker-compose.with-postgres.yml, which refuses to start without it. Not needed when DATABASE_URL is set, since the URL carries the password. Outside the Docker image, Tymeslot refuses to start without one or the other.

Default: tymeslot (Docker image)  ·  Example: a_secure_password

DATABASE_POOL_SIZE Optional

Number of PostgreSQL connections Tymeslot keeps open. The Docker image defaults to 10; Cloudron, Railway and bare releases default to 60. Keep it below your server's max_connections.

Default: 10 (Docker), 60 (other)  ·  Example: 20

✉️ Email

EMAIL_ADAPTER Optional

Email delivery method: smtp, postmark, sendgrid, mailgun, ahasend, or test to discard every message. The Docker and Railway images default to test, so until you set it, password resets and booking confirmations are silently dropped. Bare releases default to smtp, which works with any provider. An unrecognised value stops the app at startup rather than silently dropping mail.

Default: test (Docker, Railway), smtp (other)  ·  Example: smtp

Cloudron Users

On Cloudron, the sendmail addon configures email automatically. You don't need to set EMAIL_ADAPTER, SMTP_*, or EMAIL_FROM_* variables. Set EMAIL_ADAPTER only if you want a different provider.
EMAIL_FROM_NAME Required

Sender name shown in email clients. Bare releases refuse to start without it.

Default: Tymeslot (Docker, Railway)  ·  Example: Your Company

EMAIL_FROM_ADDRESS Required

Sender email address. The image default is a placeholder that most mail servers reject, so set your own before sending mail. Bare releases refuse to start without it.

Default: hello@localhost (Docker, Railway)  ·  Example: hello@yourdomain.com

EMAIL_SUPPORT_ADDRESS Optional

Support contact address. Defaults to EMAIL_FROM_ADDRESS if not set.

Default: EMAIL_FROM_ADDRESS  ·  Example: support@yourdomain.com

EMAIL_CONTACT_RECIPIENT Optional

Recipient for contact-form submissions. Defaults to EMAIL_FROM_ADDRESS if not set.

Default: EMAIL_FROM_ADDRESS  ·  Example: info@yourdomain.com

SMTP_HOST Optional

Required for the SMTP adapter. SMTP server hostname.

Default: none  ·  Example: smtp.gmail.com

SMTP_PORT Optional

SMTP server port. Use 587 for STARTTLS (recommended) or 465 for implicit TLS. Other ports use TLS when the server offers it.

Default: 587  ·  Example: 587

SMTP_SSL Optional

Set to true for a provider that serves implicit TLS on a port other than 465, such as 2465 or 8465. Without it Tymeslot opens a plain connection there, the relay waits for a TLS handshake that never comes, and the send times out. false turns implicit TLS off, including on 465.

Default: decided by port  ·  Example: true

SMTP_USERNAME Optional

SMTP authentication username. Set it together with SMTP_PASSWORD, or leave both unset for a relay that authorises by network rather than by login (a local Postfix, a company relay).

Default: none  ·  Example: hello@yourdomain.com

SMTP_PASSWORD Optional

SMTP authentication password or app password. Set it together with SMTP_USERNAME, or leave both unset for a relay that needs no login.

Default: none  ·  Example: your_smtp_password

SMTP_CACERTFILE Optional

PEM bundle containing the certificate authority that issued your mail server's certificate. Needed when the relay is your own: a self-hosted mail server serves a self-signed certificate until you point it at Let's Encrypt, and no public trust store can validate that. The bundle replaces the public store rather than adding to it.

Default: system trust store  ·  Example: /app/data/smtp-ca.pem

SMTP_TLS_VERIFY Optional

Set to none to accept whatever certificate the relay presents. Mail is still encrypted, but the server's identity is no longer checked, so an intercepted connection cannot be detected. Use it only as a last resort for a self-signed relay with no CA to trust; prefer SMTP_CACERTFILE, which is ignored while this is set to none.

Default: peer  ·  Example: none

SMTP_TLS_MIDDLEBOX_COMPAT Optional

Rarely needed. Set to true to make TLS 1.3 look like TLS 1.2 on the wire, for a firewall or proxy on the path that drops handshakes it does not recognise. Turn it on only if the relay is reachable by other mail clients but times out or closes the connection here; against a relay that does not expect it, it breaks every send instead.

Default: false  ·  Example: true

POSTMARK_API_KEY Optional

Required for the Postmark adapter. Server API token from your Postmark dashboard.

Default: none  ·  Example: your-server-api-token

SENDGRID_API_KEY Optional

Required for the SendGrid adapter. The key needs only the "Mail Send" permission.

Default: none  ·  Example: SG.your-api-key

MAILGUN_API_KEY Optional

Required for the Mailgun adapter. Sending API key from Mailgun.

Default: none  ·  Example: your-api-key

MAILGUN_DOMAIN Optional

Required for the Mailgun adapter. The sending domain verified with Mailgun.

Default: none  ·  Example: mg.yourdomain.com

MAILGUN_BASE_URL Optional

Mailgun API endpoint. Set it only for EU-hosted Mailgun accounts.

Default: https://api.mailgun.net/v3  ·  Example: https://api.eu.mailgun.net/v3

AHASEND_API_KEY Optional

Required for the AhaSend adapter. API key from the AhaSend dashboard.

Default: none  ·  Example: aha-sk-your-api-key

AHASEND_ACCOUNT_ID Optional

Required for the AhaSend adapter. The account the API key belongs to.

Default: none  ·  Example: your-account-id

🔑 Google OAuth

ENABLE_GOOGLE_AUTH Optional

Set to true (or false) to show or hide the Google button on the login and sign-up pages. Leave it unset to switch it from the admin UI instead; while it is set to one of those, it takes precedence.

Default: false  ·  Example: true

GOOGLE_CLIENT_ID Optional

Required for Google Login, Google Calendar and Google Meet. OAuth client ID from Google Cloud Console.

Default: none  ·  Example: 123456-abc.apps.googleusercontent.com

GOOGLE_CLIENT_SECRET Optional

Required for every Google feature. OAuth client secret from Google Cloud Console.

Default: none  ·  Example: GOCSPX-your-secret

GOOGLE_STATE_SECRET Optional

Required for Google Calendar and Google Meet. Random secret that signs the OAuth state parameter to block cross-site request forgery.

Default: none  ·  Example: openssl rand -hex 32

🔑 Microsoft OAuth

OUTLOOK_CLIENT_ID Optional

Required for Outlook Calendar and Microsoft Teams. The Azure app's Application (client) ID.

Default: none  ·  Example: your-azure-app-client-id

OUTLOOK_CLIENT_SECRET Optional

Required for Outlook Calendar and Microsoft Teams. The Azure app's client secret value.

Default: none  ·  Example: your-client-secret-value

OUTLOOK_STATE_SECRET Optional

Required for Outlook Calendar and Microsoft Teams. Random secret that signs the OAuth state parameter to block cross-site request forgery.

Default: none  ·  Example: openssl rand -hex 32

🎥 Video Conferencing

Tymeslot can attach a Zoom meeting to confirmed bookings. To use it, create your own Zoom Marketplace app; the Zoom integration guide walks you through it.

ZOOM_CLIENT_ID Optional

Required to use Zoom. Marketplace app Client ID.

Default: none  ·  Example: your-zoom-client-id

ZOOM_CLIENT_SECRET Optional

Required to use Zoom. Marketplace app Client Secret.

Default: none  ·  Example: your-zoom-client-secret

ZOOM_STATE_SECRET Optional

Required to use Zoom. Random secret that signs the OAuth state parameter to block cross-site request forgery.

Default: none  ·  Example: openssl rand -hex 32

ZOOM_DEAUTH_SECRET Optional

Required to use Zoom. The Marketplace app's Secret Token, used to verify the HMAC-SHA256 signature on the deauthorisation webhook Zoom posts to /auth/zoom/deauthorize. Find it under Features → Deauthorization Notification in the Marketplace app.

Default: none  ·  Example: <marketplace secret token>

ZOOM_UPDATE_SCOPE_ENABLED Optional

Requests the meeting:update:meeting scope so a rescheduled booking moves its Zoom meeting. Set to false only if your Zoom app lacks that scope; users are then not asked to reconnect for it, and the Zoom meeting keeps its original time.

Default: true  ·  Example: false

VIDEO_ROOM_RETENTION_DAYS Optional

Days after a meeting ends before Tymeslot deletes its video room, for providers whose rooms otherwise stay on the organiser's server (Nextcloud Talk). A cancelled booking's room is deleted straight away. It must be a whole number of 1 or more, or Tymeslot refuses to start. The daily clean-up looks back 30 days past this period, so lowering it by more than 30 at once leaves the rooms of meetings that ended before the new window in place.

Default: 7  ·  Example: 14

📅 Calendar Sync

WEBHOOK_BASE_URL Optional

Public HTTPS base URL that Google and Microsoft call back to for real-time calendar change notifications (Google push channels and Outlook Graph subscriptions). Usually the same as your PHX_HOST with an https:// prefix: scheme and host only, with no trailing slash or path.

When unset, calendar sync falls back to polling every 15 minutes, which works but is not instant. Set it only when the host is reachable from the public internet over HTTPS: providers run a validation handshake against the URL when a subscription is registered, so leave it unset on localhost, private-network and NAT deployments. Existing calendar connections adopt push when their owner reconnects; new connections register automatically.

Default: none (polling fallback)  ·  Example: https://tymeslot.yourdomain.com

💳 Meeting Payments (Stripe)

MEETING_PAYMENTS_ENABLED Optional

Turns on meeting payments: the payments dashboard and the per-meeting-type payment toggle. Your instance must be registered as a Stripe Connect platform; see the Stripe Payments Setup guide . Accepts true, 1, yes, false, 0 or no, in any case. Leave it unset to switch it from the admin UI instead; while it is set to one of those, it takes precedence.

Default: false  ·  Example: true

STRIPE_SECRET_KEY Optional

Your platform's Stripe secret key. Required when MEETING_PAYMENTS_ENABLED=true; the webhook secret below is read only when this is set.

Default: unset  ·  Example: sk_live_51...

STRIPE_CONNECT_WEBHOOK_SECRET Optional

Signing secret of the Connect webhook endpoint (/webhooks/stripe/connect) that receives connected-account events. Required to verify checkout, refund and dispute events for meeting payments.

Default: unset  ·  Example: whsec_...

MEETING_PAYMENTS_APPLICATION_FEE_BP Optional

Optional platform fee in basis points (100 = 1%), taken from each charge. The default of 0 takes no cut. Accepts 0 to 10000; any other value stops the app at startup.

Default: 0  ·  Example: 100

🔑 GitHub OAuth

ENABLE_GITHUB_AUTH Optional

Set to true (or false) to show or hide the GitHub button on the login and sign-up pages. Leave it unset to switch it from the admin UI instead; while it is set to one of those, it takes precedence.

Default: false  ·  Example: true

GITHUB_CLIENT_ID Optional

Required for GitHub Login. OAuth app client ID from GitHub's Developer settings.

Default: none  ·  Example: your_client_id

GITHUB_CLIENT_SECRET Optional

Required for GitHub Login. OAuth app client secret.

Default: none  ·  Example: your_client_secret

🔑 Generic OAuth / OIDC SSO

ENABLE_OAUTH_AUTH Optional

Set to true to show an SSO button on the login page. When it is on, Tymeslot refuses to start unless the client ID, client secret and the three endpoint URLs below are set. Accepts only true or false; any other value stops Tymeslot from starting. Leave it unset to switch it from the admin UI instead; while it is set, it takes precedence.

Default: false  ·  Example: true

Cloudron Users

On Cloudron, the OIDC addon turns SSO on automatically, so users can log in with their Cloudron account. You don't need to set any OAUTH_* variables. Set ENABLE_OAUTH_AUTH=false to turn SSO off. The automatic switch-on does not pin the setting, so you can still manage it from the admin UI; only an explicit value takes precedence.
OAUTH_CLIENT_ID Optional

OAuth 2.0 client ID registered with your identity provider.

Default: none  ·  Example: tymeslot

OAUTH_CLIENT_SECRET Optional

OAuth 2.0 client secret from your identity provider.

Default: none  ·  Example: your-client-secret

OAUTH_PROVIDER_URL Optional

Base URL of your identity provider. Needed only when an endpoint below is given as a relative path (such as /token), which is resolved against it.

Default: none  ·  Example: https://idp.example.com

OAUTH_AUTHORIZE_URL Optional

Authorisation endpoint. Must use HTTPS, or be a path relative to OAUTH_PROVIDER_URL.

Default: none  ·  Example: https://idp.example.com/authorize

OAUTH_TOKEN_URL Optional

Token endpoint. Must use HTTPS, or be a relative path.

Default: none  ·  Example: https://idp.example.com/token

OAUTH_USERINFO_URL Optional

Userinfo endpoint. Must use HTTPS, or be a relative path. It must return a JSON object with a sub claim (or id/user_id if OAUTH_ALLOW_ID_FALLBACK is enabled).

Default: none  ·  Example: https://idp.example.com/userinfo

OAUTH_SCOPE Optional

Space-separated list of OAuth scopes to request.

Default: openid email profile  ·  Example: openid email profile groups

OAUTH_ALLOW_ID_FALLBACK Optional

Set to true to accept id or user_id claims when the IdP does not return a standard sub claim. Enable it only for non-OIDC providers.

Default: false  ·  Example: true

OAUTH_EMAIL_VERIFIED_CLAIM Optional

How far to trust the IdP's email_verified claim. trust_absent trusts an email sent without the claim (many IdPs omit it) but not one marked false. require trusts only emails marked true; other users confirm theirs by email. ignore trusts every email the IdP returns, for IdPs that mark addresses they manage themselves as unverified.

Default: trust_absent  ·  Example: require

🛡️ reCAPTCHA

RECAPTCHA_SITE_KEY Optional

Required for reCAPTCHA. v3 site key from the Google reCAPTCHA admin console. Without both keys, verification is skipped even when the switches below are on.

Default: none  ·  Example: 6Le-your-site-key

RECAPTCHA_SECRET_KEY Optional

Required for reCAPTCHA. v3 secret key from the Google reCAPTCHA admin console.

Default: none  ·  Example: 6Le-your-secret-key

RECAPTCHA_SIGNUP_ENABLED Optional

Checks the reCAPTCHA score on the sign-up form. Accepts true or false. Leave it unset to switch it from the admin UI instead; while it is set to one of those, it takes precedence.

Default: false  ·  Example: true

RECAPTCHA_SIGNUP_MIN_SCORE Optional

Minimum reCAPTCHA score (0.0 to 1.0) a sign-up needs to pass. Higher is stricter. Leave it unset to switch it from the admin UI instead; while it is set to a number in that range, it takes precedence.

Default: 0.3  ·  Example: 0.5

RECAPTCHA_BOOKING_ENABLED Optional

Checks the reCAPTCHA score on booking forms. Accepts true or false. Leave it unset to switch it from the admin UI instead; while it is set to one of those, it takes precedence.

Default: false  ·  Example: true

RECAPTCHA_BOOKING_MIN_SCORE Optional

Minimum reCAPTCHA score (0.0 to 1.0) a booking needs to go through. Leave it unset to switch it from the admin UI instead; while it is set to a number in that range, it takes precedence.

Default: 0.3  ·  Example: 0.3

RECAPTCHA_EXPECTED_HOSTNAMES Optional

Comma-separated list of hostnames whose reCAPTCHA tokens are accepted, so a token solved on another site cannot be replayed against yours. Unset, any hostname is accepted.

Default: none  ·  Example: tymeslot.yourdomain.com

✈️ Telegram Notifications

TELEGRAM_BOT_TOKEN Optional

Bot token from @BotFather. Setting it turns on shared bot mode, which needs all three Telegram variables: the application refuses to start if the token is set without the other two.

Default: none  ·  Example: 123456789:ABCdef...

TELEGRAM_BOT_USERNAME Optional

Username of the bot (without the @ prefix). Required when TELEGRAM_BOT_TOKEN is set.

Default: none  ·  Example: MyTymeslotBot

TELEGRAM_WEBHOOK_SECRET Optional

Random secret passed to Telegram's setWebhook API and verified on every incoming webhook call. Required when TELEGRAM_BOT_TOKEN is set.

Default: none  ·  Example: openssl rand -hex 32

TELEGRAM_ENABLED Optional

Set to true to turn on Telegram in own-bot mode, where each user supplies their own bot token and chat ID. It has no effect when TELEGRAM_BOT_TOKEN is set, because the token selects shared bot mode whatever this flag says.

Default: false  ·  Example: true

💬 Slack Notifications

SLACK_CLIENT_ID Optional

Slack App client ID from api.slack.com/apps. Setting it turns on Slack notifications; together with SLACK_CLIENT_SECRET it also turns on OAuth mode, where users connect with an "Add to Slack" button.

Default: none  ·  Example: 1234567890.1234567890

SLACK_CLIENT_SECRET Optional

Slack App client secret from the same page. Required alongside SLACK_CLIENT_ID to complete the OAuth code-for-token exchange.

Default: none  ·  Example: abcdef0123456789abcdef0123456789

SLACK_ENABLED Optional

Set to true to turn on Slack in webhook-URL mode, where each user pastes their own Incoming Webhook URL. Not needed when SLACK_CLIENT_ID is set, since OAuth mode also accepts webhook URLs.

Default: false  ·  Example: true

🚨 Admin Alerts

ADMIN_ALERTS_ENABLED Optional

Set to true to receive alert emails about webhook processing failures, integration health problems and background job errors. Accepts true, 1, yes, false, 0 or no (lower case). Leave it unset to switch it from the admin UI instead; while it is set to one of those, it takes precedence, and so does ADMIN_ALERT_EMAIL for the recipient. Requires ADMIN_ALERT_EMAIL to be set.

Default: false  ·  Example: true

ADMIN_ALERT_EMAIL Optional

Address that admin alert emails go to. Required when ADMIN_ALERTS_ENABLED=true; without it, no alert email is sent. Any non-blank value works. Leave it unset to switch it from the admin UI instead; while it is set, it takes precedence.

Default: none  ·  Example: admin@yourdomain.com

🐞 Error Tracking

ERROR_TRACKING_ENABLED Optional

Tymeslot stores every unexpected error, process crash and failed background job in its own database, with the user ID and the request, LiveView or job it happened in: request path, query, parameters, headers and client IP, or job arguments. Credentials, tokens, cookies and email addresses are redacted before anything is written. An error not seen for 30 days is marked resolved, then deleted 30 days later. Set to false to store nothing; errors are then only logged, and no new-error alerts are sent.

Default: true  ·  Example: false

🔀 HTTP Proxy

HTTPS_PROXY Optional

Proxy for outbound HTTPS requests. Also accepts lowercase https_proxy. See the HTTP proxy guide for what it covers.

Default: none  ·  Example: http://proxy.corp.com:3128

HTTP_PROXY Optional

Proxy for outbound plain-HTTP requests. Also accepts lowercase http_proxy.

Default: none  ·  Example: http://proxy.corp.com:3128

NO_PROXY Optional

Comma-separated list of hostnames, *.domain wildcards, IP addresses or CIDR ranges that bypass the proxy. Also accepts lowercase no_proxy.

Default: none  ·  Example: localhost,127.0.0.1,10.0.0.0/8

📥 Inbound Reverse Proxy

TRUST_PRIVATE_CLIENT_IPS Optional

Treat forwarded addresses in private ranges as visitors rather than as proxy hops. Leave this off unless your visitors genuinely sit on a private network: by default a forwarded address in 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ::1/128 or fc00::/7 is read as a proxy in the chain, which is what keeps a two-tier proxy from collapsing every visitor onto one address. On an intranet-only deployment that same rule gives the whole LAN a single rate-limit allowance for bookings, logins and password resets; turning this on restores per-visitor limits, but only when the reverse proxy directly in front of the app is the only hop between it and the visitor. With a further private proxy tier upstream of that one (for example a LAN proxy in front of an inner nginx), the flag cannot tell that outer hop apart from a visitor, and everyone behind it still collapses onto the outer proxy's address. On an internet-facing deployment it lets a visitor forge their own address, so do not enable it there. Carrier-grade NAT (100.64.0.0/10, so Tailscale and similar) and link-local addresses are unaffected either way.

Default: false  ·  Example: true

🛡️ Outbound Network (SSRF)

In production, Tymeslot blocks outbound requests to hosts that resolve to a private, loopback or link-local address, to prevent server-side request forgery. If you self-host integrations on a private network, the switches below exempt one subsystem each. All default to blocking; leave them unset unless you run those services on an internal network.

ALLOW_PRIVATE_IPS_FOR_CALENDAR Optional

Permit calendar (CalDAV) connections whose hostname resolves to a private address. Does not affect webhooks. For backwards compatibility it also permits private video hosts, but use the video switch below for those.

Default: false  ·  Example: true

ALLOW_PRIVATE_IPS_FOR_VIDEO Optional

Permit video connections whose hostname resolves to a private address, covering self-hosted MiroTalk, Nextcloud Talk and the custom video link's connection test. Separate from the calendar switch.

Default: false  ·  Example: true

ALLOW_PRIVATE_IPS_FOR_WEBHOOKS Optional

Permit outbound webhook deliveries to URLs that resolve to a private address. Separate from the calendar and video switches, so relaxing those does not also open webhooks to internal hosts.

Default: false  ·  Example: true

🌐 Clustering

DNS_CLUSTER_QUERY Optional

DNS query used for Erlang cluster node discovery in multi-node deployments.

Default: none  ·  Example: tymeslot.internal

Minimum Configuration

The Docker image starts with only SECRET_KEY_BASE set. For a real deployment, also set PHX_HOST to your domain and configure email: EMAIL_FROM_NAME, EMAIL_FROM_ADDRESS, and an EMAIL_ADAPTER with its credentials, because the image otherwise discards every email. Add POSTGRES_PASSWORD when you use an external database. Everything else has a default or belongs to an optional feature.

❓ Frequently Asked Questions

What is the minimum set of environment variables I need to run Tymeslot?

The Docker image starts with only SECRET_KEY_BASE set, because every other variable has a default. A production deployment should set these:

  • SECRET_KEY_BASE signs sessions; the container will not start without it
  • PHX_HOST is your public domain name, used in every generated link (defaults to localhost)
  • POSTGRES_PASSWORD is needed for an external database; the bundled one defaults to tymeslot
  • EMAIL_FROM_NAME and EMAIL_FROM_ADDRESS set the sender of every email; the image defaults are placeholders

Then set EMAIL_ADAPTER and its credentials: without it, the Docker and Railway images discard every email. Use smtp with the SMTP_* variables, or set EMAIL_ADAPTER to postmark, sendgrid, mailgun or ahasend and provide an API key, plus MAILGUN_DOMAIN for Mailgun and AHASEND_ACCOUNT_ID for AhaSend.

How do I generate a value for SECRET_KEY_BASE?

Run either of these commands and use the output as the value:

mix phx.gen.secret (needs Elixir installed)

openssl rand -base64 64 | tr -d '\n' (works anywhere OpenSSL is installed)

The value must be at least 64 characters long. Use a different value for each environment, and never commit it to version control.

Can I use a .env file instead of setting environment variables directly?

Yes. Where Tymeslot looks depends on how you deploy:

  • Docker Compose: a .env file next to your docker-compose.yml is passed into the container by the bundled Compose file.
  • Docker image or standalone release: Tymeslot reads /app/data/.env and then $RELEASE_ROOT/.env at boot, taking each key from the first file that sets it. In the Docker image, /app/data is the persistent volume, so the file survives image updates. The image ignores five database keys in that file (POSTGRES_USER, POSTGRES_DB, DATABASE_URL, DATABASE_HOST, DATABASE_PORT); pass those as container variables.
  • Cloudron: Tymeslot reads /app/data/.env at boot. It sits on the persistent data volume, so it survives upgrades. Create or edit it with cloudron exec or cloudron push .

In every case, shell-supplied variables (set via the platform's CLI or dashboard) take precedence over .env entries, so the file acts as a set of defaults the environment can override key by key. Lines that cannot be parsed are skipped with a warning naming their line numbers; they never stop the boot.

Never commit a .env file containing secrets to a public repository.

What format should DATABASE_URL be in?

The standard PostgreSQL format: postgres://username:password@hostname:5432/database_name Paste the string your provider gives you unchanged. Its host, port, database, user and password take precedence over the separate DATABASE_HOST and POSTGRES_DB variables, so there is no need to set both. An sslmode parameter in the URL is honoured unless DATABASE_SSL is set.

In the Docker image, a URL naming a remote host switches the container to external mode and leaves its bundled database unused. A URL pointing at localhost keeps the bundled database, so a stale value cannot take it out of service by accident.

On Cloudron, the platform injects its own CLOUDRON_POSTGRESQL_* variables, so you set no database variables yourself.

I changed an environment variable but the application behaviour did not change. Why?

Environment variables are read only at startup. A running container keeps the values it started with, whatever you later change in your .env file or platform configuration.

With Docker Compose, run docker compose up -d after editing the .env next to the Compose file: it recreates the container with the new values. A plain docker compose restart keeps the old environment. Changes to /app/data/.env need only a restart. Settings you change in the admin UI apply without one, for any setting whose variable is unset.

🔗 Related Articles

Read Docker Self-Hosting

Docker Self-Hosting

Deploy Tymeslot with Docker Compose on any VPS or home server. Includes reverse proxy setup, file upload volumes, and WebSocket configuration for LiveView.

Read Cloudron Deployment

Cloudron Deployment

Install Tymeslot on Cloudron in minutes. PostgreSQL, SSL certificates, email relay, and automatic updates are all provisioned and managed for you.

Read Railway Deployment

Railway Deployment

Deploy Tymeslot to Railway with one click. PostgreSQL is provisioned and connected for you; add a signing secret and your email settings and your instance is live.