Installation via Docker Compose
Running via Docker Compose is the recommended, fastest and most stable way to deploy LockMyCal on your own virtual or dedicated server (VPS).
Important
LockMyCal doesn't have a publicly published Docker image yet – you build the image yourself from the source code. Docker Compose does it automatically straight from the repository (github.com/lockmycal/lockmycal, the public repository is being prepared).
System requirements
- OS: Linux (Ubuntu 22.04+, Debian 12+, Rocky Linux or macOS)
- Docker: Engine 24.0+ and Docker Compose v2+
- Hardware: At least 1 GB of RAM (2 GB recommended) and 1 vCPU
- Domain / DNS: An A/AAAA record set up pointing to your public IP address (e.g.
calendar.yourdomain.com)
1. Preparing the directory and the .env configuration
Create a directory for LockMyCal and prepare the .env configuration file:
mkdir -p /opt/lockmycal && cd /opt/lockmycal
Generate secure keys:
# Generating the key for web sessions (64 characters)
openssl rand -base64 64 | tr -d '\n'
# Generating the key for encrypting OAuth tokens to calendars (48 characters)
openssl rand -base64 48 | tr -d '\n'
Create the .env file:
# ==========================================
# REQUIRED SECURITY SETTINGS
# ==========================================
SECRET_KEY_BASE=insert_the_generated_64_character_key
DATA_ENCRYPTION_KEY=insert_the_generated_48_character_key
PHX_HOST=calendar.yourdomain.com
# ==========================================
# DATABASE SETTINGS (PostgreSQL)
# ==========================================
POSTGRES_USER=lockmycal
POSTGRES_PASSWORD=choose_a_strong_database_password # required
POSTGRES_DB=lockmycal_prod
DATABASE_URL=postgres://lockmycal:choose_a_strong_database_password@db:5432/lockmycal_prod
# ==========================================
# APPLICATION SETTINGS
# ==========================================
APP_NAME="LockMyCal"
PORT=4000
LISTEN_IP=0.0.0.0
REGISTRATION_ENABLED=true
# ==========================================
# EMAIL NOTIFICATIONS (SMTP)
# ==========================================
EMAIL_ADAPTER=smtp
EMAIL_FROM_ADDRESS="bookings@yourdomain.com"
EMAIL_FROM_NAME="LockMyCal Bookings"
SMTP_HOST=smtp.yourdomain.com
SMTP_PORT=587
SMTP_USERNAME=bookings@yourdomain.com
SMTP_PASSWORD=smtp_password
2. Creating the docker-compose.yml file
In the same folder, create the docker-compose.yml file:
services:
app:
build:
# Docker downloads the repository by itself; after the # is the branch or the version tag
context: https://github.com/lockmycal/lockmycal.git#${LOCKMYCAL_VERSION:-main}
dockerfile: Dockerfile.docker
image: lockmycal:${LOCKMYCAL_VERSION:-main}
container_name: lockmycal-app
restart: unless-stopped
env_file:
- .env
ports:
- "127.0.0.1:4000:4000"
depends_on:
db:
condition: service_healthy
volumes:
- app_data:/app/data
db:
image: postgres:17-alpine
container_name: lockmycal-db
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-lockmycal}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB:-lockmycal_prod}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-lockmycal} -d ${POSTGRES_DB:-lockmycal_prod}"]
interval: 5s
timeout: 5s
retries: 5
volumes:
pgdata:
app_data:
3. Starting and initialization
Start the containers:
docker compose up -d --build
Note
The values in .env are read literally – neither ${VAR} nor $(command) is expanded. Variables can also be stored in the file /app/data/.env in the application's volume, which the application loads at startup (the database keys POSTGRES_USER, POSTGRES_DB, DATABASE_URL, DATABASE_HOST and DATABASE_PORT are not read from it).
Watch the startup and the running of the migrations:
docker compose logs -f app
Important
First Administrator registration: The first user who registers in the empty database through the web interface after startup automatically becomes the main administrator. As soon as at least one account is created, this automatic gate closes permanently.
4. Managing the administrator via CLI / RPC
If you need to promote an existing user to administrator later, run:
docker compose exec app bin/tymeslot rpc 'Tymeslot.Release.promote_admin("user@yourdomain.com")'
To remove administrator rights:
docker compose exec app bin/tymeslot rpc 'Tymeslot.Release.demote_admin("user@yourdomain.com")'
5. Configuring the reverse proxy (Nginx)
To ensure HTTPS and SSL certificates (e.g. via Let's Encrypt / Certbot), add this configuration to Nginx:
server {
listen 80;
server_name calendar.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name calendar.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/calendar.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/calendar.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}