Docker + K3s
Let's start by explaining the relationship. In this deployment method, CdsCTF and its middleware (excluding Kubernetes) all run within Docker, while Kubernetes is used solely to provide dynamic challenge environments for CdsCTF.
You'll need to install Docker CE—and no, it's not as simple as running apt-get install docker.
To install Docker CE, follow the official documentation.
Once that's done, Docker is fully installed.
Managing multiple middleware components using standalone Docker commands can get cumbersome. That's why we use Docker Compose.
Start by creating an empty directory, and then add a compose.yml file to define the images and dependencies for CdsCTF and its middleware.
NOTE
Telemetry facilities (e.g. OpenTelemetry Collector) are not provided with this Compose. If you need observability, see Observability and deploy a Collector and backends yourself.
services:
server:
image: docker.io/elabosak233/cdsctf:1.8.1
ports:
- "127.0.0.1:8888:8888"
restart: always
volumes:
- "server:/app/data"
- "./configs:/etc/cdsctf"
depends_on:
- db
- queue
- cache
db:
image: docker.io/library/postgres:18-alpine
restart: always
environment:
POSTGRES_USER: cdsctf
POSTGRES_PASSWORD: cdsctf
POSTGRES_DB: cdsctf
volumes:
- "db:/var/lib/postgresql/18/docker"
queue:
image: docker.io/library/nats:2-alpine
restart: always
command:
- "--js"
- "--sd=/data"
volumes:
- "queue:/data"
cache:
image: docker.io/valkey/valkey:9-alpine
restart: always
volumes:
- "cache:/data"
volumes:
server:
db:
queue:
cache:If this Compose file seems overwhelming, don't hesitate to ask an LLM for help.
No custom network is defined here; Compose will use the default network. Services can reach each other by service name (e.g. db, queue, cache).
After that, in the same directory:
- Create a
configsfolder – this will be mounted into the server container at/etc/cdsctfand hold CdsCTF's config.toml. - Add a
config.tomlfile to theconfigsdirectory, containing your service configuration. - Add a
k8s.ymlfile – this file holds credentials for connecting to the K8s control plane. You can obtain it via:
cat /etc/rancher/k3s/k3s.yamlSave the output to k8s.yml.
Update your config.toml to use Docker service names (like db, queue, cache) instead of IPs for host values. Docker Compose allows services to reach each other using these names as hostnames.
Next, edit your k8s.yml. It may look like this initially:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ...
server: https://127.0.0.1:6443
name: default
# ...CdsCTF's server runs inside a container and must reach the host's K3s API via the gateway of the Compose default network, not 127.0.0.1.
To get the gateway IP, run:
docker network inspect <network_name>The default Compose network is usually named after the project directory plus _default (e.g. cdsctf_default for a directory named cdsctf). In the command output, find IPAM → Config → Gateway; that value is the gateway IP (e.g. 172.18.0.1). Update k8s.yml so server is https://<gateway_IP>:6443, for example:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ...
server: https://172.18.0.1:6443
name: default
# ...If you run docker compose up and see errors related to connecting to the Kubernetes cluster, you may need to reconfigure K3s certificates. See the Q&A section for guidance.
If successful, visiting http://127.0.0.1:8888 will reach CdsCTF. If you use http://127.0.0.1 (ports 80/443), you may get 404 page not found from K3s's Traefik. You can reconfigure Traefik; see Q&A for details.
Once resolved, run: docker compose up -d and CdsCTF should start successfully.