Docker Self-Hosting
Deploy Tymeslot using Docker and Docker Compose. Perfect for VPS hosting, home servers, or any environment with Docker support.
Run Tymeslot behind Nginx or Caddy with automatic HTTPS. Required for production deployments and OAuth integrations.
Technical Product Builder & AI Developer
Software
localhost:4000Requirements
sudo access on the serverOutcome: By the end of this guide, Tymeslot will be accessible over HTTPS at your domain, with WebSocket support enabled so all real-time features work correctly.
Tymeslot listens on port 4000 without SSL. A reverse proxy sits in front of it and handles three critical concerns:
Caddy provisions and renews Let's Encrypt certificates with zero configuration. No cron jobs, no manual renewal commands.
Run the following on your server (Ubuntu/Debian):
apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install caddy
You should see Setting up caddy in the output and the Caddy service will be enabled automatically.
Open /etc/caddy/Caddyfile in your editor and replace its contents with:
tymeslot.yourdomain.com {
reverse_proxy localhost:4000
encode gzip
}
Reload Caddy to apply the configuration:
sudo systemctl reload caddy
Within 30 seconds, Caddy will contact Let's Encrypt, obtain a certificate, and begin serving your domain over HTTPS. The first request to your domain may take a moment while the certificate is provisioned.
ss -tlnp | grep :80 to check.
Use this if Nginx is already installed on your server or required by your infrastructure team.
apt install nginx certbot python3-certbot-nginx
You should see both nginx and certbot listed as newly installed packages.
Create the file /etc/nginx/sites-available/tymeslot with the following contents. The Upgrade and Connection headers in the location block are essential โ they tell Nginx to forward WebSocket upgrade requests to Tymeslot rather than dropping them:
server {
listen 80;
server_name tymeslot.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name tymeslot.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/tymeslot.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tymeslot.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost: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;
proxy_read_timeout 86400;
}
}
# Symlink the config into sites-enabled
ln -s /etc/nginx/sites-available/tymeslot /etc/nginx/sites-enabled/
# Obtain a Let's Encrypt certificate (Certbot edits the config automatically)
sudo certbot --nginx -d tymeslot.yourdomain.com
# Test the config and reload
nginx -t && systemctl reload nginx
After certbot completes, you should see Congratulations! and the path to your new certificate. The nginx -t command should return syntax is ok and test is successful.
Upgrade and Connection "upgrade" headers are missing, the page will load but all real-time features โ availability updates, booking confirmation, search results โ will stop responding. This is the most common Nginx misconfiguration with Phoenix applications.
If you get a 502 Bad Gateway
This means Nginx cannot reach Tymeslot on port 4000. The application may not be running. Check with:
docker-compose logs tymeslot
Look for startup errors or a missing database connection in the output.
Now that Tymeslot is behind HTTPS, update your .env file so the application generates correct URLs for emails, OAuth callbacks, and links:
URL_SCHEME=https
PHX_HOST=tymeslot.yourdomain.com
Restart Tymeslot after editing the file:
docker-compose down && docker-compose up -d
You should see the containers start cleanly. Check docker-compose logs tymeslot if the application does not come up within 15 seconds.
PHX_HOST is used to construct absolute URLs for calendar invites, email notifications, and OAuth redirect URIs. If it does not match the domain your reverse proxy serves, OAuth logins will fail and email links will point to the wrong host.
When a visitor opens Tymeslot in their browser, Phoenix LiveView immediately upgrades the HTTP connection to a WebSocket. This persistent channel is how the server pushes instant updates โ booking confirmations, availability changes, form validation โ without the browser needing to poll.
The upgrade happens via a special HTTP request with the header Upgrade: websocket. By default, most reverse proxies treat this as a normal HTTP request and strip the header. You must explicitly configure the proxy to pass it through.
If the page loads but stops updating in real time
The WebSocket upgrade headers are missing from your proxy configuration. The symptom is a page that appears to load correctly but never reflects changes โ search results do not update, booking forms do not respond, and you may see a connection error banner after 30โ60 seconds.
For Nginx, confirm these three lines are inside your location / block:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
For AWS ALB, Cloudflare, or similar setups, the outermost proxy forwards headers that inner proxies must pass through. Tymeslot trusts X-Forwarded-Proto to determine the request scheme for URL generation.
If using Cloudflare, navigate to [SSL/TLS] in the Cloudflare dashboard and set the mode to Full (strict). Using Flexible causes Cloudflare to connect to your origin over HTTP while telling the origin it connected over HTTPS โ this produces redirect loops between Cloudflare and Caddy/Nginx.
WS_ALLOWED_ORIGINS to a comma-separated list of allowed origins to restrict which domains can open a WebSocket connection to your instance. This is recommended when running behind a CDN. Defaults to allowing the configured host only.
LiveView relies on a persistent WebSocket connection. When a proxy drops or mishandles the WebSocket upgrade, the browser falls back to polling and then gives up, causing the repeated disconnect banner. For Nginx, confirm all three of these directives are present inside your location / block:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
For Caddy, the reverse_proxy directive handles WebSocket upgrades automatically โ no extra configuration is needed.
A 502 means Nginx or Caddy reached the upstream address but received no response. The most common cause is that Tymeslot is not running or is still starting up. Check the container status and logs:
docker-compose ps
docker-compose logs tymeslot
Look for startup errors or a failed database connection in the output. Also confirm that the port in your proxy config matches the port Tymeslot is actually listening on โ the default is 4000.
No additional SSL configuration is required. Caddy contacts Let's Encrypt automatically, obtains a certificate for the domain in your Caddyfile, and renews it before expiry โ all without any cron jobs or manual steps on your part.
The only prerequisites are that ports 80 and 443 are open in your firewall and that your domain's DNS A record points to the server's IP address. Without both, the ACME challenge will fail and no certificate will be issued. Run ss -tlnp | grep :80 to confirm nothing else is occupying port 80.
OAuth providers (Google, Microsoft, GitHub) compare the callback URL they receive against the one registered in their developer console. If Tymeslot generates an http:// callback URL when your proxy is serving https://, the comparison fails and the login is rejected.
Ensure two things are set in your .env file and that Tymeslot has been restarted after the change:
URL_SCHEME=https
PHX_HOST=tymeslot.yourdomain.com
Also verify that your Nginx config includes proxy_set_header X-Forwarded-Proto $scheme; so Tymeslot can detect that the incoming connection arrived over HTTPS.
Yes. Set Cloudflare's SSL/TLS mode to Full (strict) in the Cloudflare dashboard. Using Flexible causes Cloudflare to connect to your origin over plain HTTP while advertising HTTPS to the browser, which creates redirect loops with Caddy and Nginx.
Additionally, ensure WebSocket support is enabled for your Cloudflare zone โ navigate to Network in the Cloudflare dashboard and confirm WebSockets is toggled on. Without it, LiveView connections will be silently dropped by Cloudflare's edge before they reach your server.
https://tymeslot.yourdomain.com in your browser. The address bar should show a padlock icon.
http://tymeslot.yourdomain.com โ it should automatically redirect to the HTTPS version.
https://tymeslot.yourdomain.com/healthcheck in a browser โ it should return a plain 200 OK.
Deploy Tymeslot using Docker and Docker Compose. Perfect for VPS hosting, home servers, or any environment with Docker support.
One-click installation on Cloudron. Automated backups, SSL certificates, and updates handled automatically.
Keep your Tymeslot instance up to date. Database migrations run automatically on startup โ upgrades are a single command.