Deploying to Docker
In this chapter, you will learn how to deploy Casdoor using Docker and Docker Compose with various reverse proxy configurations.
Prerequisites
Before starting, ensure you have:
- Docker installed on your system
- Docker Compose installed (for compose method)
- A domain name pointing to your server (for reverse proxy configurations)
Docker Deployment Methods
Choose your preferred deployment method:
- Docker Compose
- Docker Run
Using Docker Compose
Create a docker-compose.yml
file:
services:
casdoor:
image: casbin/casdoor:latest
container_name: casdoor
restart: unless-stopped
ports:
- "8000:8000"
environment:
- GIN_MODE=release
volumes:
- ./conf:/conf
- ./logs:/logs
networks:
- casdoor-network
networks:
casdoor-network:
driver: bridge
Start the service:
docker-compose up -d
Using Docker Run
Run Casdoor directly with Docker:
docker run -d \
--name casdoor \
--restart unless-stopped \
-p 8000:8000 \
-v $(pwd)/conf:/conf \
-v $(pwd)/logs:/logs \
-e GIN_MODE=release \
casbin/casdoor:latest
Reverse Proxy Configuration
For production deployments, it's recommended to use a reverse proxy with TLS certificates. Choose your preferred reverse proxy:
- Traefik (Labels)
- Traefik (Dynamic)
- Nginx
- Caddy
Traefik with Docker Labels
Create a docker-compose.yml
with Traefik labels:
services:
traefik:
image: traefik:v2.10
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/acme.json:/acme.json
command:
- --api.dashboard=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.letsencrypt.acme.email=your-email@example.com
- --certificatesresolvers.letsencrypt.acme.storage=/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
networks:
- casdoor-network
casdoor:
image: casbin/casdoor:latest
container_name: casdoor
restart: unless-stopped
environment:
- GIN_MODE=release
volumes:
- ./conf:/conf
- ./logs:/logs
labels:
- "traefik.enable=true"
- "traefik.http.routers.casdoor.rule=Host(`your-domain.com`)"
- "traefik.http.routers.casdoor.entrypoints=websecure"
- "traefik.http.routers.casdoor.tls.certresolver=letsencrypt"
- "traefik.http.services.casdoor.loadbalancer.server.port=8000"
networks:
- casdoor-network
networks:
casdoor-network:
driver: bridge
Create the acme.json file:
touch traefik/acme.json
chmod 600 traefik/acme.json
Traefik with Dynamic Configuration
Create a docker-compose.yml
with dynamic configuration:
services:
traefik:
image: traefik:v2.10
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/acme.json:/acme.json
- ./traefik/traefik.yml:/etc/traefik/traefik.yml
- ./traefik/dynamic.yml:/etc/traefik/dynamic.yml
command:
- --configfile=/etc/traefik/traefik.yml
networks:
- casdoor-network
casdoor:
image: casbin/casdoor:latest
container_name: casdoor
restart: unless-stopped
environment:
- GIN_MODE=release
volumes:
- ./conf:/conf
- ./logs:/logs
networks:
- casdoor-network
networks:
casdoor-network:
driver: bridge
Create traefik/traefik.yml
:
api:
dashboard: true
entryPoints:
web:
address: ":80"
http:
redirections:
entrypoint:
to: websecure
scheme: https
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
directory: /etc/traefik
watch: true
certificatesResolvers:
letsencrypt:
acme:
email: your-email@example.com
storage: /acme.json
httpChallenge:
entryPoint: web
Create traefik/dynamic.yml
:
http:
routers:
casdoor:
rule: "Host(`your-domain.com`)"
service: casdoor
tls:
certResolver: letsencrypt
entryPoints:
- websecure
services:
casdoor:
loadBalancer:
servers:
- url: "http://casdoor:8000"
Nginx with Let's Encrypt
Create a docker-compose.yml
with Nginx:
services:
nginx:
image: nginx:alpine
container_name: nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/conf.d:/etc/nginx/conf.d
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
depends_on:
- casdoor
networks:
- casdoor-network
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
command: certonly --webroot --webroot-path=/var/www/certbot --email your-email@example.com --agree-tos --no-eff-email -d your-domain.com
casdoor:
image: casbin/casdoor:latest
container_name: casdoor
restart: unless-stopped
environment:
- GIN_MODE=release
volumes:
- ./conf:/conf
- ./logs:/logs
networks:
- casdoor-network
networks:
casdoor-network:
driver: bridge
Create nginx/conf.d/default.conf
:
server {
listen 80;
server_name your-domain.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
location / {
proxy_pass http://casdoor:8000;
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;
}
}
Initialize SSL certificates:
# Create directories
mkdir -p certbot/conf certbot/www nginx/conf.d
# Get initial certificate
docker-compose run --rm certbot
# Add to crontab for renewal
echo "0 12 * * * docker-compose run --rm certbot renew" | crontab -
Caddy with Automatic HTTPS
Create a docker-compose.yml
with Caddy:
services:
caddy:
image: caddy:2-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- casdoor
networks:
- casdoor-network
casdoor:
image: casbin/casdoor:latest
container_name: casdoor
restart: unless-stopped
environment:
- GIN_MODE=release
volumes:
- ./conf:/conf
- ./logs:/logs
networks:
- casdoor-network
volumes:
caddy_data:
caddy_config:
networks:
casdoor-network:
driver: bridge
Create caddy/Caddyfile
:
your-domain.com {
reverse_proxy casdoor:8000 {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Proto {scheme}
}
}
Configuration
Create the necessary configuration directories
mkdir -p conf logs
Download the Casdoor configuration files
wget https://raw.githubusercontent.com/casdoor/casdoor/master/conf/app.conf -O conf/app.conf
wget https://raw.githubusercontent.com/casdoor/casdoor/master/init_data.json.template -O conf/init_data.json
Edit conf/app.conf
to match your environment settings
The default user of Casdoor has a uid and gid of 1000. When using volume mapping with SQLite (or any storage requiring file permissions), ensure that the path (e.g., /folder/of/app.conf
in the example above) is accessible to uid 1000. This avoids permission errors like permission denied
when Casdoor writes to mapped volumes.
Testing
After deployment, visit your domain in your browser:
- Docker Run/Compose only:
http://your-server-ip:8000
- With Reverse Proxy:
https://your-domain.com
Troubleshooting
Check container logs
# For docker-compose
docker-compose logs casdoor
# For docker run
docker logs casdoor
Verify reverse proxy configuration
# Check if containers are running
docker ps
# Test connectivity
curl -I http://localhost:8000
SSL Certificate Issues
- Ensure your domain points to the correct server IP
- Check that ports 80 and 443 are open in your firewall
- Verify DNS propagation with
nslookup your-domain.com