Deploy Casdoor with Docker
This guide explains how to run Casdoor in production with Docker or Docker Compose and how to put a reverse proxy with HTTPS in front of it.
Learning outcomes
- Prepare the configuration files that the Casdoor container reads.
- Start Casdoor with Docker Compose or
docker run. - Serve Casdoor over HTTPS through Traefik, Nginx, or Caddy.
- Check that the deployment works and find the cause when it doesn't.
What you need
- Docker, and Docker Compose for the Compose setups
- A supported database that the container can reach
- For HTTPS: a domain name that points to your server, and ports 80 and 443 open
Casdoor Cloud gives you a dedicated Casdoor instance that we host and keep upgraded for you, from $25/month with no per-user fees.
Prepare the configuration
-
Create the directories that you mount into the container:
mkdir -p conf logs -
Download the default 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.conffor your environment. At least set the database connection. See Configure the database and the Configuration reference.
Casdoor runs as uid and gid 1000 inside the container. Make the mounted directories readable by uid 1000, and writable if Casdoor writes to them, for example for logs or a SQLite file. Otherwise Casdoor fails with permission denied.
Start Casdoor
- Docker Compose
- Docker Run
-
Create a
docker-compose.ymlfile:services:
casdoor:
image: casbin/casdoor:latest
container_name: casdoor
restart: unless-stopped
ports:
- "8000:8000"
volumes:
- ./conf:/conf
- ./logs:/logs
networks:
- casdoor-network
networks:
casdoor-network:
driver: bridge -
Start the service:
docker compose up -d
Run the container:
docker run -d \
--name casdoor \
--restart unless-stopped \
-p 8000:8000 \
-v $(pwd)/conf:/conf \
-v $(pwd)/logs:/logs \
casbin/casdoor:latest
Casdoor now listens on port 8000 of the server.
Add a reverse proxy with HTTPS
In production, serve Casdoor over HTTPS through a reverse proxy. In the following examples, replace your-domain.com with your domain and your-email@example.com with your email address.
- Traefik (Labels)
- Traefik (Dynamic)
- Nginx
- Caddy
Traefik reads its routes from the labels of the Casdoor container and gets certificates from Let's Encrypt.
-
Create a
docker-compose.ymlfile: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
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 file in which Traefik stores the certificates:
touch traefik/acme.json
chmod 600 traefik/acme.json -
Start the services with
docker compose up -d.
Traefik reads its routes from a configuration file instead of container labels.
-
Create a
docker-compose.ymlfile: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
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" -
Start the services with
docker compose up -d.
Nginx terminates TLS with a certificate that Certbot gets from Let's Encrypt.
-
Create a
docker-compose.ymlfile: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
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;
}
} -
Get the first certificate and schedule the renewal:
# 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 - -
Start the services with
docker compose up -d.
Caddy gets and renews certificates on its own.
-
Create a
docker-compose.ymlfile: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
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
} -
Start the services with
docker compose up -d.
Verify the deployment
Open Casdoor in a browser:
| Setup | URL |
|---|---|
Docker Compose or docker run without a proxy | http://<your-server-ip>:8000 |
| With a reverse proxy | https://your-domain.com |
The Casdoor sign-in page appears. Sign in as built-in/admin with the password 123, and then change the password.
Troubleshooting
Casdoor doesn't start
Read the container logs:
# For Docker Compose
docker compose logs casdoor
# For docker run
docker logs casdoor
The proxy doesn't reach Casdoor
Check that the containers run and that Casdoor answers on port 8000:
# Check if containers are running
docker ps
# Test connectivity
curl -I http://localhost:8000
The certificate isn't issued
- Check that your domain points to the IP address of the server, for example with
nslookup your-domain.com. - Check that the firewall allows ports 80 and 443.