Installation
Preserve development data
The named PostgreSQL and Compose volumes persist between phases. Never use docker compose down -v, destructive database refreshes, or PostgreSQL-backed RefreshDatabase tests. The supported test command forces in-memory SQLite.
Development requirements
- Docker Engine with the Compose plugin
- GNU Make
- Python 3 for real-runtime qualification
- at least 8 GiB RAM for the complete local topology
- free ports
8080,8081,8082,8444,8445,9443,9191,1053/tcp,1053/udp,9090, and9093
The normal workflow builds PHP, Composer, Node, Go, PostgreSQL, Valkey, PowerDNS, OpenResty, and ClickHouse dependencies inside containers.
Install the development stack
git clone https://github.com/vaheed/CDNFoundry.git cdnfoundry
cd cdnfoundry
make dev-control-up
make dev-migrate
make dev-upmake dev-control-up starts only the services needed to run Laravel migrations. After the explicit migration, make dev-up builds and starts the full topology, including Grafana's read-only PostgreSQL role, development PKI, and GeoIP data. Neither startup target runs Laravel or PowerDNS migrations implicitly.
Create the first administrator:
docker compose -f compose.dev.yml exec core \
php artisan cdnf:admin:create \
--name="Local Administrator" \
--email="admin@example.test"The command prompts twice for a password and rejects a duplicate email or mismatch. Open http://localhost:8080/admin.
Verify the installation
curl --fail http://localhost:8080/api/health
curl --fail http://localhost:8080/api/ready
docker compose -f compose.dev.yml psRun isolated application tests:
make dev-testThe target sets the only supported destructive-test database combination: APP_ENV=testing, DB_CONNECTION=sqlite, and DB_DATABASE=:memory:.
Run non-browser real-runtime qualification only after the stack and migrations are healthy:
make dev-e2eThis job uses real HTTP APIs, PostgreSQL, queues, DNSdist, PowerDNS, OpenResty, mutual TLS, Pebble, Vector, and ClickHouse. It does not inspect the rendered UI.
Enroll the bundled edge agents
The two OpenResty hosts run before the agents because edge IDs and one-time tokens must come from administrator-created edge rows.
- In Infrastructure → Edges, create edge A and edge B.
- Copy the displayed UUID and bootstrap token for each.
- Create the ignored local environment:
cp .env.dev.example .env.dev
chmod 600 .env.dev- Set
CDNF_DEV_EDGE_A_ID,CDNF_DEV_EDGE_A_BOOTSTRAP_TOKEN,CDNF_DEV_EDGE_B_ID, andCDNF_DEV_EDGE_B_BOOTSTRAP_TOKEN. - Start and inspect the agents:
make dev-edge-up
make dev-edge-status- After both identities are registered and heartbeats are fresh, remove both bootstrap-token values from
.env.dev.
Named agent volumes retain issued identities. Losing a volume requires the administrator Rotate identity workflow. Its one-time recovery modal provides the unchanged ID plus replacement token as a deployment-neutral environment block; never copy another edge's identity volume.
Production
Do not promote the development environment. Production uses published images, host-private .env.prod files, explicit PKI, explicit Laravel and PowerDNS migrations, firewalls, a tested recovery method (optionally the built-in encrypted Restic integration), and the profiles described in Production deployment.