This guide will help you run CTFd using Docker with persistent data storage. When you stop and restart the containers, all your data (database, uploads, logs, cache) will be preserved.
- Docker Desktop installed on Windows
- Docker Compose (included with Docker Desktop)
-
Clone and navigate to the CTFd directory:
cd c:\Users\PRANXTEN\Documents\CTFPanel
-
Create environment file:
Copy-Item .env.example .env -
Edit the .env file and change the default passwords:
- Open
.envin your preferred text editor - Change
MYSQL_ROOT_PASSWORD,MYSQL_PASSWORD, andSECRET_KEYto secure values
- Open
-
Start CTFd:
docker-compose up -d
-
Access CTFd:
- Open your browser and go to
http://localhost - Follow the setup wizard to configure your CTF
- Open your browser and go to
-
Stop CTFd:
docker-compose down
Your CTFd data is stored in the .data/ directory with the following structure:
.data/
├── mysql/ # Database files
├── redis/ # Cache data
├── CTFd/
│ ├── logs/ # CTFd logs
│ └── uploads/ # Uploaded files
Important: Never delete the .data/ directory unless you want to permanently lose all your CTF data.
The Docker setup includes:
- ctfd: Main CTFd application (port 8000)
- nginx: Reverse proxy (port 80)
- db: MariaDB database (internal)
- cache: Redis cache (internal)
| Variable | Description | Default |
|---|---|---|
MYSQL_ROOT_PASSWORD |
MySQL root password | ctfd_root_password_change_me |
MYSQL_PASSWORD |
CTFd database password | ctfd_password_change_me |
SECRET_KEY |
Flask secret key | your_secret_key_here_change_me |
WORKERS |
Number of gunicorn workers | 1 |
You can also modify CTFd/config.ini for additional configuration options like:
- Email settings
- Upload providers (filesystem/S3)
- Security settings
- Theme settings
# All services
docker-compose logs
# Specific service
docker-compose logs ctfd
docker-compose logs db# Restart all
docker-compose restart
# Restart specific service
docker-compose restart ctfd# Stop containers
docker-compose down
# Rebuild CTFd image
docker-compose build ctfd
# Start with new image
docker-compose up -d# Create backup directory
mkdir backups
# Backup database
docker-compose exec db mysqldump -u ctfd -p ctfd > backups/ctfd_backup_$(Get-Date -Format "yyyy-MM-dd").sql
# Backup entire data directory
Compress-Archive -Path .data -DestinationPath "backups/ctfd_data_backup_$(Get-Date -Format 'yyyy-MM-dd').zip"# Access MySQL shell
docker-compose exec db mysql -u ctfd -p ctfdIf port 80 is already in use, modify docker-compose.yml:
nginx:
ports:
- "8080:80" # Change to different portOn Windows, if you encounter permission issues with volumes:
# Ensure Docker Desktop has access to your drive
# Go to Docker Desktop Settings > Resources > File Sharing-
Ensure the database container is healthy:
docker-compose ps -
Check database logs:
docker-compose logs db
To start fresh (
docker-compose down -v
Remove-Item -Recurse -Force .data
docker-compose up -d- Change default passwords in the
.envfile - Use HTTPS in production by configuring SSL certificates in nginx
- Firewall rules - only expose necessary ports (80/443)
- Regular backups of the
.datadirectory - Keep Docker images updated by running
docker-compose pullperiodically
For production deployments:
- Use a reverse proxy with SSL termination
- Set up automated backups
- Monitor resource usage
- Configure log rotation
- Use Docker secrets for sensitive data
- Set up health checks
- Consider using external managed databases
For CTFd-specific issues, refer to: