Phase 1 task 1.1 lands. Directus 11.17.4 boots locally end-to-end against a TimescaleDB+PostGIS container; admin UI serves at :8055, admin bootstrap from env vars works, named volumes preserve data across down/up cycles. Scaffold: - Dockerfile — FROM directus/directus:11.17.4. Pre-installs postgresql16-client (ahead of task 1.2's db-init runner needing psql). Bakes in /directus/snapshots, /directus/db-init, /directus/scripts, /directus/extensions, /directus/entrypoint.sh. - compose.dev.yaml — db (timescale/timescaledb-ha:pg16.6-ts2.17.2-all) + directus (local build), healthchecks, named volumes directus-pg-data + directus-uploads. - entrypoint.sh — placeholder using upstream's actual flow (node cli.js bootstrap && pm2-runtime start ecosystem.config.cjs); the real db-init -> schema apply -> start wrapper lands in task 1.7. - package.json — scripts-only (dev, dev:down, dev:reset, schema:snapshot, schema:apply, db:init), no runtime deps. - .env.example — sectioned, fully documented, KEY/SECRET marked required with generation hints. - .gitignore, .dockerignore — match the processor service conventions. - snapshots/, db-init/, scripts/, extensions/ — empty with .gitkeep, filled by later Phase 1 tasks (1.3, 1.6) and Phase 5. Lessons locked in (against the empirical pnpm dev boot): - timescale/timescaledb-ha:pg16-latest does NOT exist on Docker Hub. Pin a concrete version (we used pg16.6-ts2.17.2-all). - This image's data directory is /home/postgres/pgdata/data, not /pgdata or /var/lib/postgresql/data. PGDATA env var and the volume mount must both target it. - The -all variant bundles PostGIS binaries but the extension is not auto-created on the directus database; CREATE EXTENSION lands in Phase 2 alongside the geofences/SLZs/waypoints collections. - The upstream image's CMD is bootstrap + pm2-runtime, not a simple cli.js start. Bypassing pm2 would lose crash recovery. These corrections folded into 01-project-scaffold.md (deliverable line + Done section), 08-gitea-ci-dryrun.md (CI service tag), and the inline comments in compose.dev.yaml so future implementers don't re-discover them. Status: ROADMAP marks 1.1 done, Phase 1 in progress, 1.2 next.
7.5 KiB
Task 1.1 — Project scaffold
Phase: 1 — Slice 1 schema + deploy pipeline
Status: 🟩 Done
Depends on: None
Wiki refs: docs/wiki/entities/directus.md, docs/wiki/synthesis/directus-schema-draft.md
Goal
Initialize the directus/ service folder with the directory layout from the Phase 1 README, the config files needed for local Docker compose dev, and a minimal compose.dev.yaml that boots Directus + TimescaleDB so the next tasks have something to iterate against. No Directus collections are created in this task — that starts in 1.4.
Deliverables
directus/Dockerfile—FROM directus/directus:11.x, copiessnapshots/,db-init/,scripts/,entrypoint.sh,extensions/into the image. SetsENTRYPOINT ["/directus/entrypoint.sh"]. (Concrete entrypoint contents land in task 1.7; for now create a placeholder that justexecs the upstream entrypoint.)directus/compose.dev.yaml— two services:db:timescale/timescaledb-ha:pg16.6-ts2.17.2-all(the-allvariant bundles PostGIS binaries; the:pg16-latestfloating tag does NOT exist on Docker Hub — pin a concrete TS+PG version). Volume-mapped Postgres data dir, healthcheck.directus: built from localDockerfile, depends ondbhealthy, env vars for DB connection +KEY+SECRET+ admin bootstrap, port8055exposed.
directus/package.json— minimal, only for npm scripts (no runtime deps). Scripts:schema:snapshot—bash scripts/schema-snapshot.sh(script body lands in 1.6)schema:apply—bash scripts/schema-apply.shdb:init—bash scripts/apply-db-init.shdev—docker compose -f compose.dev.yaml up --builddev:down—docker compose -f compose.dev.yaml downdev:reset—docker compose -f compose.dev.yaml down -v && docker compose -f compose.dev.yaml up --build
directus/.env.example— full list of env vars with descriptions and defaults. Required:DB_HOST,DB_PORT,DB_DATABASE,DB_USER,DB_PASSWORD,KEY,SECRET,ADMIN_EMAIL,ADMIN_PASSWORD,PUBLIC_URL. Plus optional:LOG_LEVEL,LOG_STYLE,CACHE_ENABLED,CORS_ENABLED,CORS_ORIGIN,WEBSOCKETS_ENABLED.directus/.gitignore—node_modules/,.env,.env.local,*.log,directus-data/(the local Postgres volume mount, if used).directus/.dockerignore—.git/,.planning/,node_modules/,.env*,*.mdexceptREADME.md,compose.dev.yaml(compose isn't part of the image),directus-data/.- Empty placeholder directories with
.gitkeep:snapshots/(1.6 fills it)db-init/(1.3 fills it)scripts/(1.2, 1.6 fill it)extensions/(Phase 5)
directus/entrypoint.sh— placeholder that simplyexec /directus/cli.js start(or whatever the upstream image's start command is). Real wrapper lands in 1.7.directus/README.mdalready exists from this scaffold pass — verify it's accurate.
Specification
- Postgres image choice. Pin to a TimescaleDB image that includes PostgreSQL 16. PostGIS will be installed via
db-init/in Phase 2; the base image must supportCREATE EXTENSION postgis(most TimescaleDB-HA images do). Document the pinned tag in compose.dev.yaml. - Volume policy in compose.dev.yaml. Use a named volume (
directus-pg-data) sodev:downpreserves data anddev:resetwipes it. - No secrets committed.
.envis gitignored..env.examplecarries placeholder values only. - No bind mounts of
snapshots/ordb-init/in compose.dev.yaml. The image bakes them in. (Implementer can override with a bind mount during local iteration but the committed file does not.) - Entrypoint is a placeholder in this task. Real flow (db-init → schema apply → start) lands in 1.7. Keep the placeholder simple to unblock 1.4 testing.
Acceptance criteria
pnpm installsucceeds (no runtime deps; lockfile generated).docker compose -f compose.dev.yaml up --buildboots Directus successfully against a fresh TimescaleDB container.http://localhost:8055serves the Directus admin login.- First-time bootstrap with
ADMIN_EMAIL/ADMIN_PASSWORDfrom.envworks. pnpm dev:downstops the stack, preserves the volume.pnpm dev:resetwipes the volume and reboots clean.- No collection definitions exist yet — the Directus instance is empty by design.
Risks / open questions
- TimescaleDB-HA image PostGIS support. Verify the chosen tag includes
postgisextension binaries (or document the alternative — e.g. switching topostgis/postgis:16-masterwith manual TimescaleDB install). Capture the answer in this task's Done section. - Directus 11.x patch version. Pin a specific tag (e.g.
11.5.1) rather than11.xfor reproducible builds. Update the pin via PR when bumping.
Done
Pending commit by user. All deliverables created in the same working tree pass.
Open question resolutions (corrected against live pnpm dev boot 2026-05-01):
- TimescaleDB-HA image tag.
:pg16-latestdoes not exist on Docker Hub (the agent's initial pin failed at pull time). The empirically-verified tag istimescale/timescaledb-ha:pg16.6-ts2.17.2-all. The-allsuffix bundles PostGIS binaries. - PGDATA path. Not
/pgdata(the agent's first guess); the actual data directory baked into this image is/home/postgres/pgdata/data.PGDATA: /pgdataplus a volume mount to/pgdataproduced "could not change permissions of directory" errors at initdb. Fixed by setting bothPGDATAand the volume target to/home/postgres/pgdata/data. - PostGIS extension. Binaries are bundled in the
-allimage but the extension is not auto-created on thedirectusdatabase. Directus boot logs warn:PostGIS isn't installed. Geometry type support will be limited.Resolution:CREATE EXTENSION IF NOT EXISTS postgis;lands in db-init when geometry types are needed (Phase 2). Phase 1 has no geometry columns so the warning is benign. - Directus version pin:
directus/directus:11.17.4confirmed to exist on Docker Hub. Used as the image pin.
Deviations from task spec:
entrypoint.shdelegates tonode cli.js bootstrap && pm2-runtime start ecosystem.config.cjs(the upstream image's actual CMD) rather thanexec /directus/cli.js start. The upstream image uses pm2-runtime to manage the process; bypassing it would skip crash recovery and signal handling that pm2 provides. Thebootstrapstep is idempotent (safe to run every boot) and handles admin user creation.compose.dev.yamlsetsPGDATA: /home/postgres/pgdata/dataand mounts the named volume to the same path. Required by thetimescaledb-ha:*-allimage; mounting elsewhere fails initdb.Dockerfileinstallspostgresql16-clientvia apk so thatscripts/apply-db-init.sh(task 1.2) can invokepsqlwithout adding that dependency later.README.mdupdated: pinned11.x→11.17.4; CI section notes workflow file is pending (task 1.8).
Live boot acceptance (2026-05-01):
pnpm dev against fresh volumes succeeded: db became healthy, Directus ran 60+ system migrations, PM2 cluster started, server bound at http://0.0.0.0:8055, GraphQL Subscriptions and WebSocket Server started. Admin bootstrap (creating first admin role + admin user) completed. One benign warning ("PostGIS isn't installed" — see Open question resolutions above). One harmless migration warning about a non-existent constraint on directus_comments_collection_foreign (a Directus internal migration's expected idempotent guard, not caused by this task). All Phase 1 task 1.1 acceptance criteria met.