Local development
This guide runs the Node control plane on your machine and, optionally, an agent in a local Linux VM. For a remote installation, see Cloudflare or Docker Compose. For code structure and plugins, see Development.
Requirements
- Node.js 22.12+ and pnpm 10.17+.
- Go 1.27.3, pinned in
agent/go.mod, to build or test the agent. - macOS and Lima for the VM workflow.
Start the control plane
pnpm install
pnpm configure
pnpm devChoose Local development. Setup writes app/.env, generates secrets and can create a GitHub App. pnpm dev applies pending migrations to the databases in app/.data/, and stops if a migration fails. If you reuse a database, keep its existing encryption key.
Open http://localhost:7429. Startup fails if the port is in use. Configure sign-in. The first account becomes administrator unless DEVPUSH_ADMIN_EMAIL is set. Node development does not start the background scheduler; see Background work.
Local deployment VM
pnpm vm:setup
pnpm agent:build:linuxvm:setup creates and starts the devpush-test VM with Docker. The VM has 2 CPUs, 4 GiB of memory, a 30 GiB disk and VZ/vzNAT networking. Set these values in app/.env and restart the app:
DEVPUSH_BASE_URL=http://host.lima.internal:7429
BETTER_AUTH_URL=http://localhost:7429
DEVPUSH_MCP_URL=http://localhost:7429/mcp
DEVPUSH_AGENT_BINARY_DIR=/absolute/path/to/devpush/binThe VM reaches your machine through host.lima.internal, while the browser keeps using localhost. Add a server in devpush and pass the URL from its generated install command:
pnpm vm:install 'http://host.lima.internal:7429/api/install/agent/INSTALL_TOKEN'This installs the agent and pins the VM's private IP with --address. When the server connects, create a project. Visit URLs use HTTP and nip.io, such as http://my-app-192-168-64-7.nip.io. DNS resolvers that block private-address answers may also block nip.io.
If the VM's IP changes, update DEVPUSH_ROUTING_ADDRESS in the VM's /etc/devpush/agent.env and restart devpush-agent. Existing aliases are not rewritten; see Routing addresses.
Helper commands:
pnpm dev:start,pnpm dev:stopandpnpm dev:restartmanage the app in the background. The log is.tmp/devpush-dev.log.dev:startalso starts the VM, unless you setDEVPUSH_REQUIRE_LIMA=0.DEVPUSH_LIMA_INSTANCE=NAMEselects another VM.pnpm vm:stopstops the VM.pnpm vm:destroydeletes the VM and its data. It does not remove the server record from the dashboard.
Tests
Run tests from the repository root. Tests use temporary stores; never point them at live data. Start with the narrowest check for your change:
CI workflows define the automated gates. A configured workflow does not prove that a given revision passed.
Schema changes
pnpm --dir app db:generate
pnpm --dir app db:generate:logs
pnpm --dir app db:migrate
pnpm test:integrationdb:migrate applies both main and log stores. Generate only the migration sets you changed, and commit the SQL with its Drizzle metadata. Never rewrite an applied migration. Use db:push only for disposable local experiments.
VM and smoke tests
pnpm vm:test runs real Compose deployment, Traefik routing and HTTPS certificate tests against devpush-test. It refuses an enrolled VM. If you have used the VM manually, preserve any data you need and run pnpm vm:destroy first. pnpm smoke:agent runs the same tests against another isolated Docker host. Never use a host that serves other agents.
Other checks:
pnpm validate:localruns the routine checks plus deployment and Docker routing smoke tests. SetDEVPUSH_SKIP_DOCKER_SMOKE=1only to deliberately skip the routing smoke test.pnpm build && pnpm smoke:node,pnpm smoke:devandpnpm --dir app smoke:worker d1run isolated Node, development and Worker servers.pnpm validate:productionbuilds and checks the Compose image and stack. It requires Docker.pnpm smoke:deploycreates fixture data in the current database configuration. Run it only against isolated stores.pnpm validate:remoteandpnpm validate:remote:proofinstall and verify an agent on a dedicated test host. Readpnpm validate:remote -- --helpfirst, and treat the install command as a credential.pnpm setup:hetzner -- --listlists server options. Only--createmakes a billable server.--destroyremoves tracked resources.
Local simulators do not prove real provider credentials, DNS or GitHub webhooks.
Troubleshooting
- Agent disconnected: From the VM, request
http://host.lima.internal:7429/api/health. Check the laptop listener and the URL in/etc/devpush/agent.env. - Visit fails: Confirm the VM IP and the route's service, port and exact hostname. DNS resolution alone does not configure Traefik.
- OAuth fails: Check the registered origin and callback against Authentication.