Quick Start
Run a Manager on your own machine with Docker Compose, then run the Go backend and the Vue frontend directly for development. The Configuration page explains every setting used here.
The whole platform
pfy dev up starts Manager, database, Pocket ID and proxy together and pfy dev seed creates a project, tenant, application and machine user for you. See the Zero-to-App Local Quickstart and the pfy dev reference. The rest of this page runs the Manager repository on its own.
Prerequisites
- Docker with the Compose plugin
- Git
For running the backend and frontend outside Docker you also need:
- Go 1.26 or newer (
go.modsaysgo 1.26.0) - Node.js 20 or newer and pnpm; the UI's
devscript runspnpm i && vite, so plainnpmis not enough - PostgreSQL 14 or newer
- The Atlas CLI (
atlas), for themake migrate-*targets and the migration round-trip tests. Applying migrations does not need it:manager -migratecarries them
1. Clone the repository
git clone https://github.com/ProductifyFW/manager.git
cd manager2. Create a local config file
cp config.example.yml config.local.ymlconfig.local.yml is gitignored. The Manager reads it in preference to config.yml, and any PFY_* environment variable overrides both. The example file already contains a working development setup:
env: development # anything else turns the production checks on
run_mode: both # API server and trigger executor in one process
port: 8080
health_port: 8081
db:
host: db # the Compose service name; use localhost outside Compose
port: 5432
user: postgres
password: password
name: productify # the key is `name`, not `dbname`
sslmode: disable # the default is `require`; the dev database has no TLS
dev_user_override:
enabled: true
id: ca53cb60-e72b-47b5-a7a3-9fa48dda681c
email: user@productify.dev
name: Test User
username: testenv: development and dev_user_override belong together
env defaults to production. In production the Manager refuses to start without a database password, a Pocket ID API key, an encryption key and a proxy secret, and it rejects sslmode: disable.
dev_user_override is an authentication bypass: every request is treated as the configured user. It only works when env is development, and the Manager refuses to start if it is enabled anywhere else. Without it, a Manager that has no proxy in front of it answers every API request with 401 (see Troubleshooting).
3. Start the services
docker compose upCompose starts four services (see docker-compose.yml; a fifth, executor, is behind the production profile and does not start by default):
| Service | What it does | Host port |
|---|---|---|
db | PostgreSQL 18 | 5432 |
migrate | One-shot job that runs atlas migrate apply from ent/migrate/migrations. The Manager never migrates on boot, so a fresh database needs this first. | none |
backend | The Manager, run with air for live reload (run_mode: both) | 8080 (API), 8081 (health) |
frontend | The Vue dev server (pnpm run dev) | 5173 |
4. Open the application
- Frontend UI: http://localhost:5173. In development the API port also proxies unknown paths to the frontend dev server, so http://localhost:8080 shows the UI too.
- GraphQL playground (development only): http://localhost:8080/gql. The endpoint it talks to is
POST /query. - Liveness: http://localhost:8081/healthz returns
{"status":"healthy","version":...,"build_time":...}. - Readiness: http://localhost:8081/readyz returns
200when the database is reachable and migrated,503with areasonotherwise. It may also carry awarningslist (for example that no orchestrator is configured); warnings never change the status code.
The first request fails with 500 until the dev user exists
When a request arrives for a user the Manager has never seen, it creates the user row and asks Pocket ID for the username. With dev_user_override and no Pocket ID running, that lookup fails and every UI and GraphQL request returns 500 (Processing error; the log shows pocketid request failed: Get "http://pocketid.localhost/api/users?..."). The username in dev_user_override is read into the config but never used for this.
Create the user row yourself once and the lookup is skipped from then on:
docker compose exec db psql -U postgres -d productify -c \
"INSERT INTO users (id, external_name, external_username, external_email, role, created_at)
VALUES ('ca53cb60-e72b-47b5-a7a3-9fa48dda681c', 'Test User', 'test', 'user@productify.dev', 'administrator', now())
ON CONFLICT DO NOTHING;"role is the system role. administrator lets the dev user use the system-administrator surfaces (audit export, access review, system roles); use normal if you do not want that.
Developing without Docker
Database and migrations
Point config.local.yml at your PostgreSQL (db.host: localhost), then apply the migrations once:
make build # builds bin/manager
bin/manager -migrate # reads the same db.* / PFY_DB_* settings as the servermanager -migrate applies the migrations compiled into the binary, through the Atlas Go SDK. It needs no atlas executable and no checkout, and can run from any directory. PFY_MIGRATIONS_DIR points it at an on-disk directory instead, read through the same code path including the atlas.sum check, for an operator holding a newer migration set than the binary.
make migrate-apply DB_URL='postgres://postgres:password@localhost:5432/productify?sslmode=disable' is the equivalent through the Atlas CLI. That one does need atlas on PATH and must run from the repository root, where ent/migrate/migrations lives. Both write the same revision history, so a database can be migrated by either and continued by the other.
Backend with live reload
# Install Air (module path github.com/air-verse/air)
go install github.com/air-verse/air@latest
# Run with Air (the repo ships an .air.toml that builds cmd/server)
airWithout Air: make run (which is go run ./cmd/server).
Frontend
cd ui
pnpm install
pnpm run devThe dev server listens on http://localhost:5173 with hot module replacement. The backend's proxy_target setting (default http://localhost:5173) is where the API port forwards non-API requests in development.
Running tests
go test ./... # all packages
go test -race ./... # with the race detector
go test ./pkg/machineuser/... # one package
go test -cover ./... # with coverageThe migration round-trip tests are skipped unless a throwaway PostgreSQL and atlas are available; make test-migrations provides both and fails instead of skipping.
Code generation
GraphQL resolvers and the Ent client are generated:
go generate ./... # Ent + gqlgen
make generate # same thingRun it after changing:
- GraphQL schema files in
graph/*.graphql - Ent schema files in
ent/schema/
Environment variables
Every setting can be overridden with an environment variable. The name is PFY_ plus the YAML path in upper case, with _ between nested keys:
export PFY_ENV=development
export PFY_PORT=8080
export PFY_DB_HOST=localhost
export PFY_DB_PASSWORD=secret
export PFY_RUN_MODE=bothSee Configuration for the full list.
Troubleshooting
Port already in use
Compose maps the container's fixed ports to the same host ports (8080:8080, 8081:8081, 5173:5173). To use a different host port, change the left-hand side of the mapping in docker-compose.yml, for example "8090:8080".
When you run the binary directly, change port in config.local.yml or set PFY_PORT=8090.
Every request returns 401
If /healthz answers but the API returns 401 for everything, the proxy-secret gate is failing closed. The body says why:
{"error":"manager misconfiguration: no proxy secret is configured (PFY_PROXY_SECRET is unset and dev_user_override is not enabled), so the manager cannot recognise the trusted proxy and rejects every request. ..."}Either set PFY_PROXY_SECRET to the same value as the proxy's PFY_MANAGER_TOKEN, or, when running without a proxy, set env: development and dev_user_override.enabled: true. The Manager also logs a repeated WARNING: PFY_PROXY_SECRET is unset ... at startup in this state.
Every request returns 500
See the note above: the dev user's first request needs Pocket ID or a pre-created user row.
relation "..." does not exist
The database has no schema. The Manager never migrates on boot, in any run mode. docker compose up runs the one-shot migrate service for you; if you run the binary directly, run bin/manager -migrate first (see Database and migrations). /readyz reports this condition as 503 with a reason.
The published container image can migrate its own database: docker run --rm -e PFY_DB_HOST=... ghcr.io/productifyfw/manager:<tag> /app/manager -migrate.
Database connection issues
docker compose logs db # check the database
docker compose down -v # drop the volume; the migrate service reapplies the schema
docker compose upFrontend build failures
cd ui
rm -rf node_modules
pnpm install
pnpm run devNext steps
- Configuration: every setting, with defaults and production requirements
- Trigger System: scheduled callbacks to your backends
- Multi-tenancy: projects, tenants and applications
- Deployment: running the Manager in production