# Caddy Reverse Proxy Configuration for Kopia **Date**: April 27, 2026 **Status**: Configuration Ready for Deployment **Purpose**: HTTPS reverse proxy for kopia.jgitta.com → siklos:51515 --- ## Overview This guide configures Caddy as a reverse proxy to expose Kopia Web UI securely at `https://kopia.jgitta.com` with: - ✅ Automatic HTTPS/TLS termination - ✅ Domain-based routing from jgitta.com - ✅ Transparent proxy to http://192.168.88.27:51515 - ✅ Security headers and best practices --- ## Current Setup | Component | Location | Details | |-----------|----------|---------| | **Kopia Web UI** | siklos (192.168.88.27:51515) | Running in Docker container | | **DNS Record** | MikroTik (192.168.88.1) | kopia.jgitta.com → 192.168.88.27 | | **Reverse Proxy** | Caddy (to be deployed) | siklos @ port 443 (HTTPS) | | **Internal Network** | 192.168.88.0/24 | Private LAN (jgitta.com) | --- ## Caddy Configuration ### File Location ``` /srv/docker/caddy/Caddyfile ``` ### Configuration Content ```caddy # Kopia Web UI Reverse Proxy kopia.jgitta.com { # Reverse proxy to Kopia Web UI reverse_proxy 127.0.0.1:51515 { # Pass original headers header_upstream X-Forwarded-Proto {scheme} header_upstream X-Forwarded-Host {http.request.host} header_upstream X-Forwarded-For {http.request.remote.host} # WebSocket support (Kopia uses WebSockets) header_upstream Connection {http.request.header.Connection} header_upstream Upgrade {http.request.header.Upgrade} } # Security headers header { # Browser security X-Content-Type-Options "nosniff" X-Frame-Options "SAMEORIGIN" X-XSS-Protection "1; mode=block" # HTTPS enforcement Strict-Transport-Security "max-age=31536000; includeSubDomains" # Cache control Cache-Control "public, max-age=3600" } # Logging log { output stdout format json } # Enable TLS (automatic via Let's Encrypt for public domains, self-signed for internal) tls internal } # Optional: Redirect HTTP to HTTPS http://kopia.jgitta.com { redir https://kopia.jgitta.com{uri} permanent } ``` --- ## Docker Compose for Caddy ### Location ``` /srv/docker/caddy/docker-compose.yml ``` ### Configuration ```yaml version: '3.8' services: caddy: image: caddy:latest container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" volumes: # Caddyfile configuration - ./Caddyfile:/etc/caddy/Caddyfile:ro # Persistent data (certificates, config) - caddy_data:/data - caddy_config:/config # Logs - ./logs:/var/log/caddy environment: # For internal TLS, Caddy will use self-signed certificates # For public domains, configure DNS for Let's Encrypt validation CADDY_ACME_CA: "https://acme-v02.api.letsencrypt.org/directory" networks: - docker_network labels: com.example.description: "Caddy reverse proxy for homelab services" # Health check healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s volumes: caddy_data: caddy_config: networks: docker_network: driver: bridge ``` --- ## Deployment Steps ### Step 1: Create Directory Structure ```bash # SSH to siklos ssh jgitta@192.168.88.27 # Create Caddy directory if it doesn't exist sudo mkdir -p /srv/docker/caddy/logs # Set permissions sudo chmod 755 /srv/docker/caddy ``` ### Step 2: Create Caddyfile ```bash # Create the Caddyfile with the configuration above sudo tee /srv/docker/caddy/Caddyfile > /dev/null << 'EOF' # [Insert Caddyfile content from above] EOF # Verify syntax sudo docker run --rm -v /srv/docker/caddy/Caddyfile:/etc/caddy/Caddyfile caddy validate ``` ### Step 3: Create Docker Compose ```bash # Create docker-compose.yml sudo tee /srv/docker/caddy/docker-compose.yml > /dev/null << 'EOF' # [Insert docker-compose.yml content from above] EOF ``` ### Step 4: Start Caddy Service ```bash cd /srv/docker/caddy # Start the container sudo docker-compose up -d # Verify it's running sudo docker-compose ps # Check logs sudo docker-compose logs -f caddy ``` ### Step 5: Verify Configuration ```bash # Test connectivity from siklos curl -k https://kopia.jgitta.com/ # Test from another host on the network # Navigate to https://kopia.jgitta.com in browser # Check certificate (should show self-signed for internal TLS) openssl s_client -connect kopia.jgitta.com:443 /dev/null | openssl x509 -text -noout ``` --- ## Network Access ### From Internal Network (192.168.88.0/24) ``` https://kopia.jgitta.com → Caddy (HTTPS) ↓ http://127.0.0.1:51515 → Kopia Web UI ``` ### DNS Resolution ``` kopia.jgitta.com → 192.168.88.27 (MikroTik configured) ↓ Caddy port 443 ``` --- ## Security Considerations ### TLS Certificates - **Internal Setup**: Using `tls internal` (self-signed, no validation needed) - **For Public Access**: Implement Let's Encrypt with DNS01 challenge - **ACME CA**: Already configured for Let's Encrypt in docker-compose.yml ### Access Control - Limited to internal network (192.168.88.0/24) via MikroTik firewall - No exposure to external internet (unless router configured otherwise) - Basic authentication can be added to Caddyfile if needed ### Headers & Security - X-Frame-Options prevents clickjacking - X-Content-Type-Options prevents MIME sniffing - Strict-Transport-Security enforces HTTPS - Cache-Control manages client caching --- ## Adding Basic Authentication (Optional) To protect Kopia Web UI with username/password: ```caddy kopia.jgitta.com { # Generate hash: caddy hash-password basicauth / { admin JDJhJDEwJFlOQ3I4... # Replace with generated hash } reverse_proxy 127.0.0.1:51515 { # ... rest of config } } ``` Then login with: - Username: `admin` - Password: [what you hashed] --- ## Monitoring & Maintenance ### Check Service Status ```bash sudo docker-compose -f /srv/docker/caddy/docker-compose.yml ps ``` ### View Logs ```bash # Real-time logs sudo docker-compose -f /srv/docker/caddy/docker-compose.yml logs -f # Last 50 lines sudo docker-compose -f /srv/docker/caddy/docker-compose.yml logs --tail=50 ``` ### Reload Configuration (Zero Downtime) ```bash # If you modify the Caddyfile: sudo docker-compose -f /srv/docker/caddy/docker-compose.yml restart ``` ### Verify Certificate ```bash ls -la /var/lib/docker/volumes/caddy_data/_data/caddy/certificates/ ``` --- ## Troubleshooting ### Caddy won't start ```bash # Check syntax sudo docker run --rm -v /srv/docker/caddy/Caddyfile:/etc/caddy/Caddyfile caddy validate # View detailed logs sudo docker-compose logs caddy ``` ### Cannot connect to kopia.jgitta.com 1. Verify DNS: `nslookup kopia.jgitta.com` (should return 192.168.88.27) 2. Check Caddy is running: `sudo docker-compose ps` 3. Test connectivity: `curl -v https://kopia.jgitta.com/ -k` 4. Check firewall on MikroTik allows port 443 ### Certificate errors - If using self-signed (internal TLS), browser will warn — this is expected - Add exception in browser, or use `curl -k` to ignore certificate warnings ### Kopia backend unreachable - Verify Kopia container is running on siklos: `docker ps | grep kopia` - Test local port: `curl http://localhost:51515` (from siklos) - Check port forwarding in docker-compose.yml --- ## Integration with Existing Services ### Other Reverse Proxies If you have existing Caddy or Nginx: 1. Add kopia.jgitta.com block to existing configuration 2. Or use this as a separate Caddy instance on different port 3. Configure MikroTik to route traffic appropriately ### Gitea Integration Once deployed: - Add to INFRASTRUCTURE-COMPLETE.md under "Web Access" section - Document kopia.jgitta.com URL as HTTPS endpoint - Update Gitea repository with Caddy configuration --- ## Related Documentation - Kopia Backup Setup: `kopia-nextcloud-backblaze-setup.md` - Infrastructure Complete: `INFRASTRUCTURE-COMPLETE.md` - Caddy Documentation: https://caddyserver.com/docs/caddyfile - Docker Compose: https://docs.docker.com/compose/ --- **Status**: Ready for deployment **Last Updated**: April 27, 2026 **Next Step**: Deploy Caddy configuration on siklos and test HTTPS access