Use this file to discover all available pages before exploring further.
Running Timeful on localhost:3002 is fine for local evaluation, but production deployments need TLS termination, a real domain name, and correctly registered OAuth redirect URIs. This page walks through every step using either Nginx or Caddy as the reverse proxy, then covers the .env changes required to tie everything together.
The Timeful Docker stack exposes a single port — 3002 — from the frontend Nginx container. Your host-level reverse proxy forwards HTTPS traffic to that port and handles TLS certificate management.
Nginx
Caddy
Install Nginx on the host and obtain a TLS certificate (e.g. via Certbot):
Caddy automatically provisions and renews TLS certificates via Let’s Encrypt — no manual certificate management required.Install Caddy (see caddyserver.com/docs/install), then create a Caddyfile:
yourdomain.com { reverse_proxy localhost:3002}
caddy start --config /etc/caddy/Caddyfile
Or add Caddy as a sidecar container inside docker-compose.ghcr.yml:
After your reverse proxy is serving traffic, update the backend to match your domain by editing .env and restarting.
1
Set BASE_URL in .env
BASE_URL is used to construct OAuth callback URLs, email links, and Stripe redirect URLs. It must exactly match the origin your users reach Timeful at — including the protocol and without a trailing slash.
BASE_URL=https://yourdomain.com
2
Set CORS_ALLOWED_ORIGINS in .env
Add every origin that will send API requests to the backend. Localhost ports are always permitted automatically.
# Single domainCORS_ALLOWED_ORIGINS=https://yourdomain.com# Apex + wwwCORS_ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com# Multiple subdomainsCORS_ALLOWED_ORIGINS=https://timeful.corp.com,https://staging.corp.com
Always use the full URL with protocol (https://). No trailing slashes. When this variable is set, the default Timeful production domains are replaced, not appended — include every origin you need.
3
Update Google OAuth redirect URI
In Google Cloud Console → APIs & Services → Credentials, edit your OAuth 2.0 Client ID and add the following Authorised redirect URI:
https://yourdomain.com/api/auth/google/callback
Remove any stale localhost entries if this instance is public-facing only.
4
Restart the backend
Configuration changes in .env are picked up on container startup. A restart is sufficient — no rebuild is needed.
# .env — production custom domain deployment# RequiredENCRYPTION_KEY=<output of: openssl rand -base64 32># Custom domainBASE_URL=https://timeful.example.comCORS_ALLOWED_ORIGINS=https://timeful.example.com,https://www.timeful.example.com# Google OAuth (required for user accounts and calendar integration)CLIENT_ID=123456789-abc.apps.googleusercontent.comCLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxxxxxx# Microsoft OAuth (optional — Outlook calendar integration)MICROSOFT_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxMICROSOFT_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx# Premium: unlock all features for all users (no Stripe required)SELF_HOSTED_PREMIUM=true# Email notifications (optional)GMAIL_APP_PASSWORD=xxxx-xxxx-xxxx-xxxxSCHEJ_EMAIL_ADDRESS=noreply@example.com# Slack alerts (optional)SLACK_PROD_WEBHOOK_URL=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXX