A modern Docker setup for hosting Python websites with Caddy web server and SSH access for Fabric deployments.
- Python 3.13.1: Supported Python version with common extensions.
- Caddy: Modern web server with automatic HTTPS.
- SSH Access: For automated deployments with Fabric.
- Automatic Site Discovery: Just add your site folder and it works.
- Development Domains: Test with .local domains that map to production folders.
- Automatic WWW Redirection: For second-level domains (e.g., example.com → www.example.com).
- Auto-HTTPS: Certificates are automatically generated on-demand.
- Environment Separation: Development and production environments managed through Docker Compose override.
- Docker and Docker Compose installed on your system.
- SSH key for deployment access.
-
Clone this repository:
git clone https://github.com/derafu/docker-python-caddy-server.git cd docker-python-caddy-server -
Add your SSH public key to
config/ssh/authorized_keysfor admin, and default deployment, access:cat ~/.ssh/id_rsa.pub > config/ssh/authorized_keys
-
Build and start the container:
docker-compose up -d
The
-dparameter runs it in detached mode (background).
Check that the container is running:
docker-compose psView container logs:
docker-compose logs -fThe -f parameter allows you to follow logs in real-time.
-
Create the site directory structure:
mkdir -p sites/www.example.com/ cd sites/www.example.com/ -
Create project Django
python3 -m venv venv source venv/bin/activate pip install django django-admin startproject example .
-
Run a single site manually
/scripts/start_sites.sh www.example.com
Note: If no site is specified, the script will start all available sites under /var/www/sites.
-
Access the site at:
- Production mode: https://www.example.com (requires DNS configuration).
- Development mode: https://www.example.com.local:8443 (requires local hosts entry).
For local development, add to your /etc/hosts file:
127.0.0.1 www.example.com.local
docker-python-caddy-server/
├── Dockerfile # Python + Caddy server base image definition
├── docker-compose.yml # Production Docker services and volumes
├── .env # Environment variables for docker-compose
├── LICENSE # Project license
├── README.md # Main project documentation
Configuration
├── config/
│ ├── bash/
│ │ └── bashrc # Shell prompt / history tweaks for container user
│ ├── caddy/
│ │ └── Caddyfile # Caddy reverse proxy rules (HTTPS, domains, routing)
│ ├── cron/
│ │ └── logrotate # Cron job for rotating logs periodically
│ ├── logrotate/
│ │ ├── caddy # Logrotate rules for Caddy
│ │ └── gunicorn # Logrotate rules for Gunicorn
│ ├── ssh/
│ │ ├── authorized_keys # Public keys for SSH login (e.g., deploy access)
│ │ └── sshd_config # SSH server settings (OpenSSH)
│ └── supervisor/
│ └── supervisord.conf # Supervisor config to manage processes (Caddy, Gunicorn, etc.)
Development
├── sites/ # Django projects, one per domain
│ └── www.example.com/ # Project folder for www.example.com
Helper Scripts
├── scripts/
│ └── start_sites.sh # Auto-detect and launch Gunicorn for each site under /sites
│ └── start_gunicorn_supervisord.sh # Auto-detect and launch Gunicorn for each site under /sites
Documentation
├── docs/
│ ├── docker.md # Notes and recommendations for Docker usage
This project uses Docker Compose's override functionality to separate development and production configurations:
The base docker-compose.yml contains the minimal configuration needed for production deployment. It:
- Sets up required environment variables.
- Defines essential ports (HTTP, HTTPS, SSH).
- Doesn't mount external volumes.
The docker-compose.override.yml file adds development-specific settings:
- Adds additional development ports (e.g., management interface).
- Mounts local volumes for easy site development.
-
Development: Docker Compose automatically merges both files:
docker-compose up -d
-
Production: Use only the base configuration:
docker-compose -f docker-compose.yml up -d
Connect to the container via SSH:
ssh admin@localhost -p 2222Access the container shell:
docker exec -it derafu-sites-server-python-caddy bashRestart Caddy web server:
docker exec -it derafu-sites-server-python-caddy supervisorctl restart caddydocker-compose downRebuild for development:
docker-compose build --no-cache
docker-compose up -dRebuild for production:
docker-compose -f docker-compose.yml build --no-cache
docker-compose -f docker-compose.yml up -d-
Create the site directory structure:
mkdir -p sites/www.newsite.com/ cd sites/www.newsite.com/ -
Create project Django
python3 -m venv venv source venv/bin/activate pip install django django-admin startproject newsite .
NOTE: Es importante que el archivo requirements.txt contenga la dependencia gunicorn (Ejemplo gunicorn==23.0.0)
-
No server restart required! Caddy automatically detects new sites.
-
For local development, add to your hosts file:
127.0.0.1 www.newsite.com.local
Customize behavior through environment variables:
| Variable | Description | Default |
|---|---|---|
SERVER_NAME |
Name for the docker container | derafu-sites-server |
CADDY_DEBUG |
Enable debug mode with debug |
(empty) |
CADDY_EMAIL |
Email for Let's Encrypt | admin@example.com |
CADDY_HTTPS_ISSUER |
TLS issuer (internal, acme) | internal |
CADDY_HTTPS_ALLOW_ANY_HOST |
Allow any host for TLS | false |
CADDY_LOG_SIZE |
Log file max size | 100mb |
CADDY_LOG_KEEP |
Number of log files to keep | 5 |
WWW_ROOT_PATH |
Web root path | /var/www/sites |
WWW_USER |
WWW and SSH user in the container | admin |
WWW_GROUP |
WWW group in the container | www-data |
HTTP_PORT |
HTTP port in host | 8080 |
HTTPS_PORT |
HTTPS port in host | 8443 |
SSH_PORT |
SSH port in host | 2222 |
The server handles domains in the following way:
-
Development domains: Any domain ending with
.local(e.g.,www.example.com.local)- Maps to the same directory as its production counterpart.
- Uses internal self-signed certificates.
-
Production domains:
- Redirects from non-www to www for second-level domains.
- Automatically obtains and manages Let's Encrypt certificates (issuer
acme).
If you're having issues with SSL certificates in development:
- Ensure your browser trusts self-signed certificates.
- Try using HTTP instead of HTTPS for local development.
If you encounter permission issues:
docker exec -it derafu-sites-server-python-caddy chown -R admin:www-data /var/www/sitesLogs are available in the container and can be accessed with:
docker exec -it derafu-sites-server-python-caddy cat /var/log/caddy/access.logFor advanced configurations, modify the Caddyfile at config/caddy/Caddyfile.
./restart_services.sh./restart_services.sh proyecto1./restart_services.sh supervisor- Esto permite que el script actúe como watchdog: detecta fallas en Gunicorn y lo reinicia.
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This package is open-sourced software licensed under the MIT license.