Reverse Proxy Configuration (Nginx, Caddy, Traefik, Apache)

For a production deployment of LockMyCal, we recommend placing the application behind a reverse proxy server (e.g. Caddy, Nginx, Traefik or Apache). The reverse proxy provides:

  1. Automatic issuing and renewal of HTTPS / SSL certificates (Let's Encrypt).
  2. Critically important WebSocket support for Elixir / Phoenix LiveView.
  3. Forwarding the correct client IP addresses (X-Forwarded-For).

Important

LockMyCal uses Phoenix LiveView for a reactive real-time interface. Your reverse proxy MUST have WebSocket connection forwarding configured correctly (the Upgrade and Connection headers).

Tip

Do you run the application under several hostnames? List them all in WS_ALLOWED_ORIGINS. If the instance is reachable only from an internal network and visitors have private IP addresses, set TRUST_PRIVATE_CLIENT_IPS=true – otherwise everyone would share a single request limit. Don't turn this option on for a publicly accessible instance.


Caddy manages SSL certificates automatically and supports WebSockets natively without any special configuration:

bookings.yourdomain.com {
    reverse_proxy localhost:4000
}

Option B: Nginx

A complete virtual host configuration for Nginx with WebSocket and HTTP/2 support:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name bookings.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name bookings.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/bookings.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/bookings.yourdomain.com/privkey.pem;

    client_max_body_size 20M;

    location / {
        proxy_pass http://127.0.0.1:4000;
        proxy_http_version 1.1;

        # WebSocket support
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Headers for correct identification of the client and protocol
        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;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

Option C: Traefik (Docker Labels)

In the docker-compose.yml file, add to the LockMyCal service:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.lockmycal.rule=Host(`bookings.yourdomain.com`)"
  - "traefik.http.routers.lockmycal.entrypoints=websecure"
  - "traefik.http.routers.lockmycal.tls.certresolver=myresolver"
  - "traefik.http.services.lockmycal.loadbalancer.server.port=4000"

Option D: Apache HTTP Server

Enable the modules: a2enmod proxy proxy_http proxy_wstunnel ssl rewrite headers

<VirtualHost *:443>
    ServerName bookings.yourdomain.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/bookings.yourdomain.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/bookings.yourdomain.com/privkey.pem

    # WebSocket forwarding
    RewriteEngine on
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/?(.*) "ws://127.0.0.1:4000/$1" [P,L]

    ProxyPass / http://127.0.0.1:4000/
    ProxyPassReverse / http://127.0.0.1:4000/

    ProxyPreserveHost On
    RequestHeader set X-Forwarded-Proto "https"
</VirtualHost>