Getting Started

Local development

This guide covers contributor local development. For supported OSes and required tooling, see Prerequisites. For layout and service dependencies, see Development architecture. For common failures, see Dev console troubleshooting.

TurboPanel is not a monorepo. Clone the five sibling repos into $HOME (~/dev, ~/daemon, ~/instance, ~/ui, ~/website). The dev repository is the entry point — it installs the dev console and orchestrates the stack via Ansible.

Fresh-clone → working dev

  1. Clone the five repos into $HOME (~/dev, ~/daemon, ~/instance, ~/ui, ~/website).
  2. From ~/dev, run ./console → prereqs, pinned Node, pnpm install, TUI launch (exports TURBOPANEL_MODE=development, TURBOPANEL_DEV_ROOT, TURBOPANEL_<DIR>_REPO).
  3. Start dev stack → daemon bootstraps as the dev user, writes /etc/turbopanel/daemon.env, runs the dev/orchestration overlay: runtimes into /opt/turbopanel/vendor, systemd units + Docker (postgres/redis/rabbitmq/mailpit) as the dev user, mutable data under FHS trees dev-user-owned. No tp / tpctrl / tpcache accounts created.
  4. Open https://localhost:8443 (or dev http://localhost:8880); edit source in place under $HOME.

Quick start

1. Bootstrap the dev console

One-liner (fresh machine):

curl -fsSL dev.turbopanel.sh | sh

The one-liner clones or updates ~/dev (or ${TURBOPANEL_DEV_ROOT}/dev) and launches ./console automatically.

Or, from an existing checkout:

cd ~/dev
sh scripts/develop.sh   # update dev repo only
./console

2. Start the dev stack

From ./console, choose Start dev stack in the Actions area. The console:

3. Open

ServiceURL
App (Caddy → UI + API, HTTPS)https://localhost:8443
App (Caddy → UI + API, dev plaintext)http://localhost:8880 — co-located dev only; requires TURBOPANEL_DEV_HTTP_CONTROL_PLANE=1
API health (HTTPS)https://localhost:8443/api/health
API health (dev plaintext)http://localhost:8880/api/health
Website (docs + API reference)http://localhost:19820 (when running separately)
Mailpithttp://localhost:8025 (when configured)

Trust the platform CA at instance/certs/ca.crt in your browser to avoid TLS warnings on :8443. The :8880 entrypoint is a dev-only plaintext mirror of :8443 — gated by TURBOPANEL_DEV_HTTP_CONTROL_PLANE=1 (set automatically on co-located dev) — and works the same for TURBOPANEL_INSTANCE_RUNTIME=deno and =workers because both runtimes sit behind the same Caddy proxy.

Smoke test:

curl -k https://localhost:8443/api/health
curl -k https://localhost:8443/api/client/v1/status
# Co-located dev plaintext mirror (no -k needed):
curl http://localhost:8880/api/health
curl http://localhost:8880/api/client/v1/status

Dev console areas

Use / to switch areas in ./console:

AreaContents
StatusRuntime, source checkouts, dev stack units
InstanceRuntime mode (Deno/Workers), instance unit status, switch action
DeveloperFleet, services, shell, database, … (when instance.sock is present)
ActionsInstall daemon, start stack, follow logs, build mode, refresh, quit

In the Developer area: ↑↓ picks a section, Enter opens it, Esc returns to the section list.

Instance runtime modes

TURBOPANEL_INSTANCE_RUNTIME in /etc/turbopanel/daemon.env selects how the instance runs locally. Switch via the Instance area in the console.

ModeBehaviour
deno (default)turbopanel-instance systemd unit + Unix socket at /run/turbopanel/instance.sock
workersStops the systemd unit, exposes Postgres on TCP (127.0.0.1:5432), expects pnpm dev in ~/instance manually

Workers mode has no Redis. Set CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE in instance/.dev.vars.

Repository layout

Source repos live under $HOME (override with TURBOPANEL_DEV_ROOT and TURBOPANEL_<DIR>_REPO):

~/
├── dev/           # turbopanel/dev — console entrypoint
├── daemon/        # turbopanel/turbopaneld
├── instance/      # Hono API (Workers + Deno)
├── ui/            # Expo frontend
└── website/       # marketing + docs (optional local run)

/opt/turbopanel/
└── vendor/
    ├── deno/2.9.4/        # pinned Deno runtime (orchestration-managed)
    ├── node/              # pinned Node runtime (console-managed)
    ├── uv/                # shared orchestration runtime (daemon bootstrap)
    ├── python/            # managed Python for Ansible venv
    └── ansible/           # ansible-playbook venv + Galaxy content

/etc/turbopanel/           # config (dev-user-owned)
/var/lib/turbopanel/       # state (dev-user-owned)
/var/log/turbopanel/       # service logs (dev-user-owned)
/run/turbopanel/           # runtime sockets (dev-user-owned)

Common tasks

Database schema

TurboPanel ships versioned SQL migrations in instance/migrations/. Co-located dev converge and Workers deploy apply them via pnpm migrate. For quick Deno-only iteration without committing migration files, use drizzle-kit push from the dev checkout:

./scripts/sync.sh          # push instance schema.ts → Postgres (interactive)
./scripts/sync.sh --force  # skip destructive-change prompts (dev only)
./scripts/introspect.sh    # pull live DB → instance schema.ts

For errors and resets, see Database troubleshooting. The dev console Database section can reset the dev instance or start Drizzle Studio.

Drizzle Studio

In the developer console Database section, start Studio via the API. The instance spawns drizzle-kit studio on 4983; open https://local.drizzle.studio?host=localhost&port=4983 in your browser.

Formatting

Each repo formats independently — run the formatter configured in that repo before committing.

Workflow diagram

Port reference

ServicePortNotes
Caddy (HTTPS app)8443Primary browser entrypoint (TLS)
Caddy (HTTP dev mirror)8880Plaintext mirror of :8443; requires TURBOPANEL_DEV_HTTP_CONTROL_PLANE=1; co-located dev only
Website19820Next.js (when run locally)
Wrangler (Workers mode)18787Not browser-facing
Expo web8081Proxied by Caddy in dev
Postgres5432Docker, 127.0.0.1:5432 (Workers mode) or Unix socket (Deno)
Mailpit web8025Dev email UI
Mailpit SMTP1025Dev email SMTP
Drizzle Studio4983HTTP API; UI at local.drizzle.studio
Edit on GitHub

Last updated on

On this page