Adding devflow to an existing project
Initialize in place
Section titled “Initialize in place”cd ~/existing-projectdevflow initinit never touches your application code. It writes .devflow.yml, installs VCS hooks (post-checkout and pre-commit; both marked so devflow uninstall-hooks removes only devflow’s), and registers the project in local state.
For non-interactive adoption, start from a small committed config and validate it:
devflow config-validatedevflow install-hooksdevflow capabilitiesAdopt existing refs and worktrees
Section titled “Adopt existing refs and worktrees”Materialize a Git ref that existed before devflow with switch:
devflow switch feature/authWorktrees you created manually with git worktree add are picked up automatically: the installed post-checkout hook detects worktree context and runs the setup (file copying + service workspace creation). To do it explicitly from inside a worktree:
devflow worktree-setupdevflow link feature/auth --from main # record immutable provenance + provision servicesMigration map from Docker Compose
Section titled “Migration map from Docker Compose”Most existing applications have three kinds of Compose services. Move them at different speeds:
| Existing Compose item | Good devflow target | Why |
|---|---|---|
| PostgreSQL/MySQL/ClickHouse | services: with type: shared or type: local | Workspace-specific data and connection URLs. |
| Redis/cache/object storage | service_type: redis or service_type: rustfs | One shared engine, per-workspace DB index/bucket. |
| App server, frontend dev server, workers, schedulers | processes.daemons with provider: native | Runs in each worktree with service env injected. |
| Search/mail/queue containers you still want as containers | service_type: generic, or keep them in Compose temporarily | Hybrid migration without a big-bang rewrite. |
env_file values that depend on ports/branches | hooks.post-switch write-env | Regenerated on every switch with the right workspace URLs. |
A practical migration is: move the database first, generate .env.local, then move app processes once the app can run on the host.
Recipe: shared Postgres + Redis
Section titled “Recipe: shared Postgres + Redis”This replaces Compose-managed Postgres/Redis containers with devflow-managed shared engines. Each workspace gets its own Postgres database and Redis DB index, while the engine containers stay global and cheap.
services: - name: app-db type: shared service_type: postgres default: true shared: image: postgres:17 port: 5432 template_branching: true - name: cache type: shared service_type: redis shared: image: redis:7
worktree: path_template: "../{repo}.{workspace}" copy_files: [.env]
hooks: post-switch: env: action: type: write-env path: .env.local vars: DATABASE_URL: "{{ service['app-db'].url }}" REDIS_URL: "{{ service['cache'].url }}" # Framework-specific aliases are fine too: CACHE_URL: "{{ service['cache'].url }}" CELERY_BROKER_URL: "{{ service['cache'].url }}"Bootstrap and seed the configured default database:
devflow service upDEFAULT_WORKSPACE="$(devflow --json list | jq -r '.default_workspace')"devflow service create "$DEFAULT_WORKSPACE"DEFAULT_DATABASE_URL="$(devflow connection "$DEFAULT_WORKSPACE" --format uri)"
# Example: import from an existing Compose Postgres container.docker compose exec -T postgres pg_dump -U postgres postgres | psql "$DEFAULT_DATABASE_URL"Now create a workspace. With template_branching: true, workspace databases are copied from the parent database:
devflow switch -c feature/authdevflow connection feature/auth --format envIf you prefer one full Docker container per workspace instead, use local containers for Postgres:
services: - name: app-db type: local service_type: postgres default: true local: image: postgres:17Add app processes
Section titled “Add app processes”Once the app can run from the host (for example through mise, uv, npm, bun, pdm, or your language’s task runner), move Compose app containers to devflow processes:
processes: provider: native # or omit for native auto_start: true auto_stop: true daemons: web: run: "mise x -- python manage.py runserver 127.0.0.1:$PORT" port: { expect: [8000], bump: 100 } ready_port: 8000 env: DATABASE_URL: "{{ service['app-db'].url }}" REDIS_URL: "{{ service['cache'].url }}" worker: run: "mise x -- celery -A app worker -l INFO" required: false env: DATABASE_URL: "{{ service['app-db'].url }}" REDIS_URL: "{{ service['cache'].url }}" scheduler: run: "mise x -- celery -A app beat -l INFO" required: false depends: [worker] env: DATABASE_URL: "{{ service['app-db'].url }}" REDIS_URL: "{{ service['cache'].url }}"$PORT is set by devflow after port bumping, and ready_port: 8000 follows the bumped port. The native supervisor is included in devflow and records process state for the CLI and desktop.
Useful commands:
devflow process start --alldevflow process statusdevflow process logs web --tail 100 --followdevflow process stop --alldevflow daemon start # keep shared engines alive; reconcile watch/retrySee Project processes for readiness checks, watch/retry, logs, proxy URLs, and approval behavior.
Hybrid rollout patterns
Section titled “Hybrid rollout patterns”You do not need to migrate everything at once.
1. Data in devflow, app still in Compose
Section titled “1. Data in devflow, app still in Compose”Generate .env.local from devflow, but keep running your app container manually. Change the app container env to point at the devflow URLs instead of Compose hostnames. This is useful when the app still depends on container-only tooling.
2. App processes in devflow, a few containers left in Compose
Section titled “2. App processes in devflow, a few containers left in Compose”Move the web/worker/scheduler commands to processes.daemons, but keep specialized containers (mailcatcher, search, queue emulator, browser test grid) in Compose until you have a reason to move them.
3. Devflow generic containers for non-branching dependencies
Section titled “3. Devflow generic containers for non-branching dependencies”For a dependency that should remain one shared local container for all workspaces:
services: - name: search type: local service_type: generic auto_workspace: false generic: image: opensearchproject/opensearch:2 port_mapping: "9200:9200" environment: discovery.type: single-nodeFor a dependency that needs one container per workspace, keep auto_workspace: true and use port_range_start rather than a fixed port mapping.
4. External or cloud-managed data
Section titled “4. External or cloud-managed data”If a team already uses a hosted branching database, use a cloud provider (neon, dblab, xata) or keep the external URL in .devflow.local.yml. Avoid committing personal credentials; commit only the shared shape and keep secrets machine-local.
Control which branches get environments
Section titled “Control which branches get environments”git: auto_create_on_workspace: true # provision services for manually added worktrees main_workspace: main workspace_filter_regex: "^(feature|fix|agent)/.*" # only these patterns exclude_workspaces: [main, master, develop] # never thesePer-machine overrides go in .devflow.local.yml (gitignored), quick toggles in environment variables — e.g. DEVFLOW_DISABLED=true to turn devflow off entirely, or DEVFLOW_CURRENT_BRANCH_DISABLED=true for just the branch you’re on.
Using mise as a task runner
Section titled “Using mise as a task runner”If your project uses mise, pair it with devflow hooks so new worktrees are immediately trusted and tooled:
hooks: post-create: mise-trust: command: "mise trust --quiet || true" condition: "file_exists:mise.toml" mise-install: command: "mise install" condition: "file_exists:mise.toml" continue_on_error: trueOr install the recipe, which detects which of mise / direnv / .env.example actually apply:
devflow hook install workspace-setupIn non-interactive automation, approve trusted hook/process command templates once:
devflow hook approvals add "mise trust --quiet || true"devflow hook approvals add "mise install"devflow hook approvals add "mise x -- python manage.py runserver 127.0.0.1:$PORT"Validation checklist
Section titled “Validation checklist”devflow config-validatedevflow hook vars --workspace maindevflow service statusdevflow connection main --format envdevflow switch -c feature/devflow-smokedevflow process start --alldevflow process statusdevflow process logs web --tail 100If a hook renders an empty service URL, create the workspace services first (devflow service create <workspace> or devflow switch -c <workspace>) and check the service name used in the template.
Team rollout
Section titled “Team rollout”.devflow.yml is committed — teammates get the same services, hooks, process definitions, and worktree layout by running devflow init (idempotent; it detects the existing config) or just devflow install-hooks + devflow switch. Hook commands from the config require a one-time approval per user before they execute.