Skip to content

Repository files navigation

DevHub

License: MIT Docker Compose Bash Docs

Local Docker development infrastructure and agentic Git worktree workflows, with shared PostgreSQL, Redis, Meilisearch, Mailpit, RabbitMQ, and isolated runtimes for Symfony, Laravel, Next.js, TanStack Start, Hono, and FastAPI DDD.

DevHub is a local Docker Compose development hub for running shared infrastructure services — PostgreSQL, MySQL, Redis, Meilisearch, Mailpit, RabbitMQ — and one-port-per-Git-worktree project runtimes across PHP, JavaScript, TypeScript, and Python applications. It replaces per-project docker-compose.yml duplication with a single Bash CLI (devhub) that manages shared dev infrastructure, Symfony/Laravel/Next.js/TanStack Start/Hono/FastAPI project registration, and isolated Git worktree runtimes on their own localhost ports.

It can also initialize an agentic worktree hub for a new or existing codebase: generated CLAUDE.md/AGENTS.md instructions, implementation and review agents, orchestration skills, and one isolated Git worktree per implementation lane.

DevHub is a Docker Compose development stack controlled by a single Bash CLI. Instead of defining database, cache, mail, search, and queue services in every project, you start them once with devhub up and connect any application through a shared Docker network.

Full documentation: makfly.github.io/dev-infra

Table of Contents

Documentation sources live in docs/ and use Tailwind CSS. Rebuild the compiled stylesheet with:

bun install
bun run docs:build

Built for teams and solo developers working on multiple web applications, APIs, or background workers that need common local infrastructure without per-project duplication.

╔══════════════════════════════════════════════════════════════╗
║ devhub CLI                                                   ║
║ shared services + project registry + worktree commands       ║
╚══════════════╤═══════════════════════════════════════════════╝
               │ writes local config
               ▼
┌──────────────────────────────────────────────────────────────┐
│ data/projects/<project>.env                                  │
│ docker/<project>/*                                           │
│ overrides/<project>-app.override.yml                         │
└──────────────╤───────────────────────────────────────────────┘
               │ starts runtime
               ▼
┌──────────────────────────────────────────────────────────────┐
│ one project runtime container                                │
│ worktrees/main -> http://localhost:8101                      │
│ worktrees/feat-x -> http://localhost:8102                    │
└──────────────╤───────────────────────────────────────────────┘
               │ service DNS
               ▼
╔══════════════════════════════════════════════════════════════╗
║ DevHub shared network                                        ║
║ infra-postgres infra-redis infra-mailpit infra-meilisearch   ║
╚══════════════════════════════════════════════════════════════╝

Legend: the committed CLI and templates generate local, ignored project runtime files. Runtime containers use the shared Docker network to reach DevHub services.

Why DevHub

  • Start once, use everywhere — one devhub up replaces duplicate docker-compose.yml files across projects.
  • Stable addresses — every service has a fixed container name (infra-postgres, infra-redis, …) and configurable host port.
  • Profiles — only run what you need. Core services start by default; RabbitMQ and Node.js are opt-in.
  • Project runtimes — Compose override files let each project add its own containers (workers, apps) that join the shared network.
  • Agentic worktree hubs — initialize a generated agent workspace where each implementation lane works in its own branch, worktree, port, and database.
  • Built-in tooling — database creation, health diagnostics, log tailing, and browser shortcuts from the CLI.

Services

Service Container Port(s) UI Profile
PostgreSQL 16 infra-postgres 5432 core
MySQL 8 infra-mysql 3306 core
Redis 7 infra-redis 6379 core
Meilisearch infra-meilisearch 7700 localhost:7700 core
Mailpit infra-mailpit 1025 / 8025 localhost:8025 core
Adminer infra-adminer 9080 localhost:9080 core
Dozzle infra-dozzle 8888 localhost:8888 core
RabbitMQ 3 infra-rabbitmq 5672 / 15672 localhost:15672 async
Node.js 22 LTS infra-node 3002 / 5173 node

Profiles

Profile Services Default
core PostgreSQL, MySQL, Redis, Meilisearch, Mailpit, Adminer, Dozzle Yes
async RabbitMQ No
node Node.js 22 LTS No
devhub up                  # core only
devhub up --with async     # core + RabbitMQ

Requirements

  • Docker Engine 24+ with Compose v2
  • Bash
  • jq (for devhub runtime / devhub down-runtime)
  • Optional: make, xdg-open (Linux) or open (macOS)

Installation

One-liner (installs the latest release to ~/.local/share/devhub, no sudo):

curl -fsSL https://raw.githubusercontent.com/dev-toolings/dev-infra/main/install-remote.sh | bash

Pin a specific version, or track the bleeding-edge main branch:

# exact release
DEVHUB_VERSION=v0.0.1 curl -fsSL https://raw.githubusercontent.com/dev-toolings/dev-infra/main/install-remote.sh | bash

# always-latest development branch (unpinned, not reproducible)
DEVHUB_VERSION=main  curl -fsSL https://raw.githubusercontent.com/dev-toolings/dev-infra/main/install-remote.sh | bash

DEVHUB_VERSION defaults to latest (newest published GitHub Release). If no release exists yet, the installer falls back to main with a warning.

Custom install path:

DEVHUB_DIR=~/tools/devhub curl -fsSL https://raw.githubusercontent.com/dev-toolings/dev-infra/main/install-remote.sh | bash

Manual (if you prefer git clone):

git clone git@github.com:dev-toolings/dev-infra.git dev-infra
cd dev-infra
cp .env.example .env
make install

Both methods symlink the CLI to ~/.local/bin/devhub and add zsh shortcuts (dh, dhup, dhps, dhdown) via ~/.config/devhub/devhub.zsh.

To skip shell integration, run the CLI directly:

./bin/devhub help

Quick Start

devhub up               # start core services
devhub ps               # show container status
devhub doctor           # health, network, and port diagnostics
devhub open adminer     # open Adminer in browser
devhub db create myapp  # create a PostgreSQL database + role

Agentic Worktree Workflows

Yes: DevHub can initialize a complete agentic worktree workflow. It registers a project once, generates the local runtime and agent workspace, then gives every implementation lane its own Git worktree, localhost port, and database context.

Use devhub project adopt <path> for an existing checkout, or devhub project init <name> --stack <stack> for a new or manually registered project. Both commands generate the agent workspace at the hub root; open your AI coding session there so orchestration can create and manage lanes through devhub wt add.

Supported stacks:

symfony
laravel
nextjs
tanstack-start
hono
fastapi-ddd

Adopt an existing project in one command

devhub project adopt migrates an existing Git checkout into a clean worktree hub: it detects the stack (root or apps/*/packages/* workspace), makes a bare clone next to the source (<path>-hub/repo.git), registers the project, creates the main worktree on its own port and database, carries the untracked .env/.env.local files over, and generates an agent workspace at the hub root: CLAUDE.md/AGENTS.md (worktree rules), .claude/agents/ (implementer lanes + read-only security/perf reviewers), .claude/skills/ (/orchestrate, /orchestrate-fast — one implementation lane = one worktree) and .claude/settings.json (devhub command allowlist). Open an AI session at the hub root and it orchestrates work through devhub wt add. devhub project init generates the same workspace.

A hub Makefile is generated too, with the project name baked in:

make run            # devhub runtime <project>
make urls           # test URLs per worktree (all apps)
make add BRANCH=feat/x
make rm SLUG=feat-x
make pr BRANCH=feat/x   # push + gh pr create
devhub project adopt ~/projects/legacy-app
# monorepo with several apps? adopted as-is (multi-app runtime), or pick one:
devhub project adopt ~/projects/monorepo --stack nextjs

When several apps are detected (e.g. apps/web Next.js + apps/api FastAPI), the project is adopted in multi-app mode: one polyglot runtime image (bun + Python), one port per app per worktree, and cross-app URLs injected in each app's env file (DEVHUB_<APP>_URL, plus API_URL/NEXT_PUBLIC_API_URL when an app is named api). wt add --json then returns an apps port map. PHP stacks are excluded from multi-app mode (use --stack to pick one app).

The source checkout is left untouched; uncommitted changes stay there and are reported at the end. Options: --name, --stack, --root, --base, --dev-command, --port-start, --port-end, --runtime-port.

Register a project manually

Register an existing Symfony API project:

devhub project init acme-api \
  --stack symfony \
  --root ~/projects/acme-api \
  --repo ~/projects/acme/api \
  --base develop

devhub wt add acme-api feat/payment develop
devhub runtime acme-api
devhub wt list acme-api

Create a new FastAPI project with a minimal DDD scaffold:

mkdir -p ~/dev
cd ~/dev
devhub project init billing-api --stack fastapi-ddd
devhub runtime billing-api

By default, devhub project init <name> creates the project runtime root in the current directory as ./<name>. Use --root <path> only when you want a different location.

Common project examples:

devhub project init crm-api --stack laravel --repo git@github.com:org/crm-api.git
devhub project init webapp --stack nextjs --repo git@github.com:org/webapp.git
devhub project init console --stack tanstack-start --repo git@github.com:org/console.git
devhub project init edge-api --stack hono --repo git@github.com:org/edge-api.git

Add worktrees after registration:

devhub wt add webapp main origin/main
devhub wt add webapp feat/search origin/main
devhub wt list webapp

Generated local files:

data/projects/<project>.env
docker/<project>/*
overrides/<project>-app.override.yml

These files are intentionally ignored by Git. Committed templates live in templates/<stack>/.

Machine-readable output

wt add, wt list, wt status, wt conflicts, wt rm, and project list accept --json and print a single JSON object (schema version "v":1, additive evolution only) on stdout, so scripts and coding agents can drive DevHub without parsing tables:

devhub wt add webapp feat/search --json
{"v":1,"status":"created","project":"webapp","slug":"feat-search","port":8102,...}

Exit codes:

Command Code Meaning
wt add 0 Worktree created
wt add 3 Already registered — the existing entry is re-printed with --json
wt add 4 No free port left in the project range
wt rm 5 Worktree has uncommitted or untracked changes (re-run with --force)
wt conflicts 6 Two lanes changed the same file, a lane changed outside its --owns fence, or migrations were touched by more than one lane

wt add also provisions the per-worktree PostgreSQL database and role when infra-postgres is running; the result is reported as db_provisioned in the JSON output. Concurrent wt add/wt rm calls are serialized with a lock on the project port registry.

CLI Reference

Command Description
devhub up [--with profile,...] Start shared services (default: core)
devhub down Stop and remove all shared containers
devhub restart [--with profile,...] Restart services
devhub ps Show service status
devhub logs [service] Follow logs
devhub open <target> Open a service UI (mailpit, adminer, dozzle, rabbitmq, meili)
devhub db create <db> [user] [pass] Create PostgreSQL database and role
devhub db import [args] Run custom import script (data/scripts/import-db.sh or DEVHUB_IMPORT_SCRIPT)
devhub db list List PostgreSQL databases
devhub project init <name> --stack <stack> Register/generate a worktree-enabled project runtime
devhub project adopt <path> One-shot adoption of an existing Git checkout into a worktree hub
devhub project list List registered local projects
devhub project show <name> Show a local project registry file
devhub wt add <project> <branch> [base] [--group <slug>] [--owns <glob>] Create/register a Git worktree on the next free port; tag it to a mission group and declare its file fence
devhub wt list <project> [--group <slug>] List worktree URLs for a project, optionally filtered by mission group
devhub wt status <project> [slug] Show http/db/runtime state per worktree
devhub wt conflicts <project> [--group <slug>] [--against <ref>] Report lanes that changed the same files, left their --owns fence, or clash on migrations
devhub wt rm <project> <slug> [--force] Remove a registered project worktree
devhub runtime <project> Start a project runtime override
devhub down-runtime <project> Stop a project runtime
devhub doctor Diagnostics: health, network, ports
devhub help Show help

Make shortcuts:

make up                          # start core
make up-async                    # start core + async
make down                        # stop all
make ps                          # status
make doctor                      # diagnostics
make runtime PROJECT=myproject   # start project runtime
make down-runtime PROJECT=myproject

Database Management

devhub db create myapp                    # db=myapp, user=myapp, pass=myapp
devhub db create myapp myuser mypassword  # explicit credentials
devhub db list                            # list all databases

Default connection strings:

PostgreSQL  postgres://test:test@localhost:5432/devhub
MySQL       mysql://test:test@localhost:3306/trading
Redis       redis://localhost:6379
Meilisearch http://localhost:7700
Mailpit     smtp://localhost:1025

Project Runtime Overrides

Add project-specific containers via Compose override files in overrides/:

overrides/<project>-app.override.yml
devhub runtime myproject       # start
devhub down-runtime myproject  # stop

Example override file:

services:
  myproject-worker:
    image: node:22-alpine
    container_name: myproject-worker
    command: ["sh", "-lc", "bun install && bun run dev"]
    volumes:
      - /path/to/project:/workspace
    working_dir: /workspace
    environment:
      - DATABASE_URL=postgres://test:test@infra-postgres:5432/myproject
      - REDIS_URL=redis://infra-redis:6379
    ports:
      - "3010:3000"
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - dev-shared-net

Override files are local by convention — the repository keeps only a .gitkeep placeholder in overrides/.

Connecting External Projects

Any Docker Compose project can reach DevHub services by joining the shared network:

# in your project's docker-compose.yml
networks:
  dev-shared-net:
    external: true

Then reference services by container name: infra-postgres, infra-mysql, infra-redis, infra-meilisearch, infra-mailpit, infra-rabbitmq.

Configuration

Copy .env.example to .env and override any default:

cp .env.example .env

All ports, credentials, and image tags are configurable. Common variables:

POSTGRES_PORT=5432
MYSQL_ROOT_PASSWORD=root
REDIS_PORT=6379
MEILI_PORT=7700
MAILPIT_UI_PORT=8025
NODE_PORT=3002
VITE_PORT=5173

Troubleshooting

devhub doctor          # full diagnostics
devhub ps              # container status
devhub logs postgres   # follow a single service
  • Network missingdevhub up, devhub db create, and devhub doctor auto-create dev-shared-net.
  • Port conflict — edit .env and change the matching *_PORT variable.

Releasing

DevHub follows Semantic Versioning. The VERSION file at the repo root is the single source of truth; devhub version prints it.

Cut a release from a clean main:

make release              # patch bump: 0.0.1 -> 0.0.2 (default)
make release BUMP=minor   # 0.0.x -> 0.1.0
make release BUMP=major   # 0.x.y -> 1.0.0
make release BUMP=1.0.0   # explicit version

# preview without touching anything
./data/scripts/release.sh patch --dry-run

The script bumps VERSION, commits chore(release): vX.Y.Z, creates an annotated vX.Y.Z tag, and (after confirmation) pushes. Pushing the tag triggers the Release workflow, which verifies the tag matches VERSION, builds a tarball, and publishes the GitHub Release with auto-generated notes. Document user-facing changes in CHANGELOG.md.

Security

This stack is for local development only. Default credentials in .env.example are intentionally simple. .env is gitignored and stays local. Dozzle mounts the Docker socket for log access. Named volumes hold developer data — do not remove them unless you want to delete local databases.

License

MIT

About

DevHub — local Docker Compose infrastructure and agentic Git worktree workflows: shared services, a generated AI-agent orchestration workspace, and isolated one-port-per-worktree runtimes for Symfony, Laravel, Next.js, TanStack Start, Hono & FastAPI.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages