Documentation Index
Fetch the complete documentation index at: https://mintlify.com/GZTimeWalker/GZCTF/llms.txt
Use this file to discover all available pages before exploring further.
GZCTF can be deployed using Docker, which is the recommended deployment method for most use cases. This guide covers Docker deployment using Docker Compose.
Prerequisites
- Docker Engine 20.10 or later
- Docker Compose v2.0 or later
- PostgreSQL database (can be deployed via Docker Compose)
- (Optional) Redis for distributed caching and SignalR backplane
Understanding the Dockerfile
GZCTF uses a multi-stage Dockerfile based on .NET 10.0 Alpine images:
/home/daytona/workspace/source/src/GZCTF/Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS build
ARG TARGETPLATFORM
COPY publish /build
RUN cp -r /build/${TARGETPLATFORM} /publish
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
LC_ALL=en_US.UTF-8
WORKDIR /app
RUN apk add --update --no-cache wget libpcap icu-data-full icu-libs \
ca-certificates libgdiplus tzdata krb5-libs && \
update-ca-certificates
COPY --from=build --chown=$APP_UID:$APP_UID /publish .
EXPOSE 8080
HEALTHCHECK --interval=5m --timeout=3s --start-period=10s --retries=1 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/healthz || exit 1
ENTRYPOINT ["dotnet", "GZCTF.dll"]
Key Features
- Multi-platform support: Uses
TARGETPLATFORM build argument for cross-platform builds
- Alpine-based: Minimal image size with required dependencies (libpcap for traffic capture, ICU for globalization)
- Health checks: Built-in health endpoint at
/healthz on port 3000
- Port configuration: Application runs on port 8080, metrics/health on port 3000
Docker Compose Setup
Create Docker Compose file
Create a docker-compose.yml file for your deployment:
version: '3.8'
services:
gzctf:
image: gztime/gzctf:latest
container_name: gzctf
restart: unless-stopped
environment:
# Database connection
- GZCTF_ConnectionStrings__Database=Host=db;Port=5432;Database=gzctf;Username=gzctf;Password=<your-db-password>
# Redis cache (optional but recommended)
- GZCTF_ConnectionStrings__RedisCache=redis:6379
# Storage configuration
- GZCTF_ConnectionStrings__Storage=disk://path=/app/files
# Email configuration (optional)
- GZCTF_EmailConfig__UserName=<smtp-username>
- GZCTF_EmailConfig__Password=<smtp-password>
- GZCTF_EmailConfig__SenderAddress=noreply@example.com
- GZCTF_EmailConfig__SenderName=GZCTF Platform
- GZCTF_EmailConfig__Smtp__Host=smtp.example.com
- GZCTF_EmailConfig__Smtp__Port=587
# Container provider (for challenges)
- GZCTF_ContainerProvider__Type=Docker
- GZCTF_ContainerProvider__PublicEntry=<your-domain-or-ip>
- GZCTF_ContainerProvider__DockerConfig__Uri=unix:///var/run/docker.sock
volumes:
- ./files:/app/files
- ./logs:/app/logs
- /var/run/docker.sock:/var/run/docker.sock # For Docker challenge management
ports:
- "8080:8080" # Application port
- "3000:3000" # Metrics and health check port
depends_on:
- db
- redis
networks:
- gzctf-network
db:
image: postgres:16-alpine
container_name: gzctf-db
restart: unless-stopped
environment:
- POSTGRES_DB=gzctf
- POSTGRES_USER=gzctf
- POSTGRES_PASSWORD=<your-db-password>
volumes:
- ./data/db:/var/lib/postgresql/data
networks:
- gzctf-network
redis:
image: redis:7-alpine
container_name: gzctf-redis
restart: unless-stopped
volumes:
- ./data/redis:/data
networks:
- gzctf-network
networks:
gzctf-network:
driver: bridge
docker-compose.minimal.yml
# Minimal setup without Redis
version: '3.8'
services:
gzctf:
image: gztime/gzctf:latest
container_name: gzctf
restart: unless-stopped
environment:
- GZCTF_ConnectionStrings__Database=Host=db;Port=5432;Database=gzctf;Username=gzctf;Password=<your-db-password>
- GZCTF_ConnectionStrings__Storage=disk://path=/app/files
- GZCTF_ContainerProvider__Type=Docker
- GZCTF_ContainerProvider__DockerConfig__Uri=unix:///var/run/docker.sock
volumes:
- ./files:/app/files
- ./logs:/app/logs
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "8080:8080"
- "3000:3000"
depends_on:
- db
networks:
- gzctf-network
db:
image: postgres:16-alpine
container_name: gzctf-db
restart: unless-stopped
environment:
- POSTGRES_DB=gzctf
- POSTGRES_USER=gzctf
- POSTGRES_PASSWORD=<your-db-password>
volumes:
- ./data/db:/var/lib/postgresql/data
networks:
- gzctf-network
networks:
gzctf-network:
driver: bridge
All configuration values can be set via environment variables using the GZCTF_ prefix. Use double underscores (__) to represent nested configuration keys.Example: ConnectionStrings:Database becomes GZCTF_ConnectionStrings__Database
Update the following values in your docker-compose.yml:
<your-db-password>: Secure password for PostgreSQL
<your-domain-or-ip>: Public IP or domain for challenge container access
SMTP settings for email notifications (optional)
Run the following command to start all services:
Check that all containers are running:
Check the health endpoint:
curl http://localhost:3000/healthz
docker compose logs -f gzctf
Storage Configuration
GZCTF supports multiple storage backends:
environment:
- GZCTF_ConnectionStrings__Storage=disk://path=/app/files
volumes:
- ./files:/app/files
Container Provider Configuration
Docker Provider
For challenge containers using Docker:
environment:
- GZCTF_ContainerProvider__Type=Docker
- GZCTF_ContainerProvider__PublicEntry=ctf.example.com
- GZCTF_ContainerProvider__DockerConfig__Uri=unix:///var/run/docker.sock
# Optional: Docker registry authentication
- GZCTF_ContainerProvider__DockerConfig__UserName=<registry-user>
- GZCTF_ContainerProvider__DockerConfig__Password=<registry-password>
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Mounting the Docker socket (/var/run/docker.sock) gives GZCTF full access to the Docker daemon. Ensure proper security measures are in place.
Kubernetes Provider
For Kubernetes-based challenge deployment, see the Kubernetes Deployment guide.
Reverse Proxy Setup
It’s recommended to run GZCTF behind a reverse proxy like Nginx or Caddy for HTTPS termination and additional security.
Nginx Configuration
server {
listen 443 ssl http2;
server_name ctf.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
client_max_body_size 64M;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_cache_bypass $http_upgrade;
}
}
Caddy Configuration
ctf.example.com {
reverse_proxy localhost:8080
encode gzip
}
When behind a reverse proxy, configure forwarded headers:
environment:
- GZCTF_ForwardedOptions__ForwardedHeaders=XForwardedFor,XForwardedProto
- GZCTF_ForwardedOptions__KnownProxies__0=172.18.0.1
Updating GZCTF
To update to the latest version:
# Pull the latest image
docker compose pull
# Restart services
docker compose up -d
Always backup your database and files before updating!
Backup and Restore
Backup Database
docker exec gzctf-db pg_dump -U gzctf gzctf > backup.sql
Restore Database
docker exec -i gzctf-db psql -U gzctf gzctf < backup.sql
Backup Files
tar -czf files-backup.tar.gz ./files
Troubleshooting
Database Connection Issues
If GZCTF fails to connect to the database:
- Verify the database is running:
docker compose ps db
- Check database logs:
docker compose logs db
- Verify connection string format
- Ensure the database has been initialized
Container Creation Failures
If challenge containers fail to start:
- Verify Docker socket is mounted correctly
- Check Docker daemon is running
- Verify
PublicEntry is correctly configured
- Check GZCTF logs:
docker compose logs gzctf
Permission Issues
If you encounter permission issues with mounted volumes:
# Fix ownership
sudo chown -R 1000:1000 ./files ./logs
Next Steps