๐ Reverse Proxy & SSL โ
A reverse proxy sits between the internet and your KitchenAsty containers. It handles HTTPS encryption, routes requests to the correct service, and provides a professional setup with proper SSL certificates.
We cover two options: Caddy (easiest, recommended) and Nginx + Certbot (most common). Choose one.
๐ ฐ๏ธ Option A: Caddy (Recommended) โ
Caddy is a modern web server that automatically obtains and renews SSL certificates from Let's Encrypt. It requires almost no configuration.
โจ Why Caddy? โ
- ๐ Automatic HTTPS โ no manual certificate setup or renewal cron jobs
- ๐ Simple configuration โ a few lines vs. hundreds for Nginx
- โก HTTP/2 and HTTP/3 out of the box
- ๐ Automatic redirects โ HTTP to HTTPS happens automatically
๐ฆ Install Caddy โ
On your server:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudflare.com/cloudflare/d/deb/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg 2>/dev/null || true
curl -1sLf 'https://dl.cloudflare.com/cloudflare/d/deb/config.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list > /dev/null 2>&1 || true
# Use the official Caddy install method
sudo apt install -y caddy 2>/dev/null || {
# Fallback: install from official Caddy repo
curl -1sLf 'https://dl.cloudflare.com/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudflare.com/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy
}Or use the simplest method:
sudo apt update
sudo apt install -y caddy๐ก
If apt install caddy isn't available on your distro, download the binary from caddyserver.com/download.
โ๏ธ Configure Caddy โ
Edit the Caddyfile:
sudo nano /etc/caddy/CaddyfileReplace the contents with:
# Admin Dashboard
admin.yourdomain.com {
reverse_proxy kitchenasty-admin:80
}
# Customer Storefront
order.yourdomain.com {
reverse_proxy kitchenasty-storefront:80
}
# API Server
api.yourdomain.com {
reverse_proxy kitchenasty-server:3000
}That's the entire configuration. Caddy will:
- ๐ Automatically obtain SSL certificates from Let's Encrypt
- ๐ Redirect HTTP to HTTPS
- โป๏ธ Renew certificates before they expire
- โก Enable HTTP/2
๐ Connect Caddy to Docker Network โ
Caddy needs to reach the Docker containers. The easiest way is to add Caddy to the same Docker network:
# Connect Caddy to the KitchenAsty Docker network
docker network connect kitchenasty caddy 2>/dev/null || trueHowever, since Caddy is installed as a system service (not a container), you need to expose the container ports on localhost instead. Update your docker-compose.prod.yml to expose ports on 127.0.0.1 only (not publicly):
server:
# ... existing config ...
ports:
- "127.0.0.1:3000:3000"
admin:
# ... existing config ...
ports:
- "127.0.0.1:5173:80"
storefront:
# ... existing config ...
ports:
- "127.0.0.1:5174:80"Then update the Caddyfile to use localhost instead of container names:
# Admin Dashboard
admin.yourdomain.com {
reverse_proxy localhost:5173
}
# Customer Storefront
order.yourdomain.com {
reverse_proxy localhost:5174
}
# API Server โ includes WebSocket support
api.yourdomain.com {
reverse_proxy localhost:3000
}๐ Start Caddy โ
sudo systemctl enable caddy
sudo systemctl restart caddy
# Check status
sudo systemctl status caddyโ Verify HTTPS โ
Wait 30-60 seconds for certificates to be issued, then visit:
- ๐
https://admin.yourdomain.comโ should show the Admin login - ๐
https://order.yourdomain.comโ should show the Storefront - ๐
https://api.yourdomain.com/api/healthโ should return{"status":"ok"}
๐ Caddy Logs โ
sudo journalctl -u caddy -f๐ ฑ๏ธ Option B: Nginx + Certbot โ
If you prefer Nginx (the industry standard) or already have it installed, follow this section.
๐ฆ Install Nginx โ
sudo apt install -y nginx
sudo systemctl enable nginx๐ Install Certbot โ
Certbot automates Let's Encrypt certificate issuance and renewal.
sudo apt install -y certbot python3-certbot-nginxโ๏ธ Create Nginx Configuration โ
First, expose the Docker container ports on 127.0.0.1 only. Edit docker-compose.prod.yml:
server:
ports:
- "127.0.0.1:3000:3000"
admin:
ports:
- "127.0.0.1:5173:80"
storefront:
ports:
- "127.0.0.1:5174:80"Restart Docker Compose:
docker compose -f docker-compose.prod.yml up -dNow create the Nginx site configs:
sudo nano /etc/nginx/sites-available/kitchenastyPaste the following:
# โโ Admin Dashboard โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
server {
listen 80;
server_name admin.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:5173;
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 API requests to the backend
location /api/ {
proxy_pass http://127.0.0.1:3000;
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 uploaded images
location /uploads/ {
proxy_pass http://127.0.0.1:3000;
}
# WebSocket support for Socket.IO (kitchen display, live orders)
location /socket.io/ {
proxy_pass http://127.0.0.1:3000;
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_read_timeout 86400;
}
}
# โโ Customer Storefront โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
server {
listen 80;
server_name order.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:5174;
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;
}
location /api/ {
proxy_pass http://127.0.0.1:3000;
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;
}
location /uploads/ {
proxy_pass http://127.0.0.1:3000;
}
location /socket.io/ {
proxy_pass http://127.0.0.1:3000;
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_read_timeout 86400;
}
}
# โโ API Server (optional dedicated subdomain) โโโโโโโ
server {
listen 80;
server_name api.yourdomain.com;
client_max_body_size 10M;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
location /socket.io/ {
proxy_pass http://127.0.0.1:3000;
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_read_timeout 86400;
}
}โ Enable the Site โ
# Enable the config
sudo ln -s /etc/nginx/sites-available/kitchenasty /etc/nginx/sites-enabled/
# Remove the default site
sudo rm -f /etc/nginx/sites-enabled/default
# Test the configuration
sudo nginx -t
# Should say: syntax is ok / test is successful
# Reload Nginx
sudo systemctl reload nginx๐ Obtain SSL Certificates โ
Run Certbot to automatically get certificates and modify the Nginx config for HTTPS:
sudo certbot --nginx \
-d admin.yourdomain.com \
-d order.yourdomain.com \
-d api.yourdomain.com \
--non-interactive \
--agree-tos \
--email your-email@example.comCertbot will:
- โ Verify you own each domain (via HTTP challenge)
- ๐ Obtain certificates from Let's Encrypt
- โ๏ธ Modify the Nginx config to use HTTPS
- ๐ Add automatic HTTP-to-HTTPS redirects
โป๏ธ Verify Automatic Renewal โ
Certbot sets up a systemd timer for automatic renewal. Verify it's active:
sudo systemctl status certbot.timer
# Test renewal (dry run)
sudo certbot renew --dry-runLet's Encrypt certificates are valid for 90 days. Certbot renews them automatically when they have less than 30 days remaining.
โ Verify HTTPS โ
Visit your sites in a browser:
- ๐
https://admin.yourdomain.com - ๐
https://order.yourdomain.com - ๐
https://api.yourdomain.com/api/health
You should see a padlock icon in the address bar.
๐ Updating CORS After SSL Setup โ
Now that your sites use HTTPS, update the CORS_ORIGINS in your .env file:
CORS_ORIGINS=https://admin.yourdomain.com,https://order.yourdomain.comThen restart the API server:
docker compose -f docker-compose.prod.yml restart server๐ง Troubleshooting โ
โ "Connection refused" errors โ
Ensure the Docker containers are running and ports are exposed on 127.0.0.1:
docker compose -f docker-compose.prod.yml ps
curl http://127.0.0.1:3000/api/health๐ Certificate issuance fails โ
- โ
Ensure DNS records point to your server (check with
dig admin.yourdomain.com) - โ
Ensure ports 80 and 443 are open in the firewall (
sudo ufw status) - โ
Ensure no other process is using port 80 (
sudo lsof -i :80)
๐ WebSocket connections fail โ
If the kitchen display or live order tracking doesn't work, verify the /socket.io/ proxy block is present in your config and includes the Upgrade and Connection headers.
โก๏ธ Next Step โ
Continue to Backups to set up automatic database backups.