Skip to content
Nautilo Documentation
Build on NautiloDevelopment

Run Nautilo from source

Choose a claimed daily-driver instance or create an isolated named instance without ending at an ownerless login screen.

A running Server is not automatically a usable development environment. The usable state is a known instance with an owner, retrievable credentials, at least one configured model route, and a client that can complete a real turn.

Choose one path deliberately:

  • reuse the claimed default instance for fast work against retained data; or
  • clone default into an isolated named instance when the work needs realistic data without writing to the daily-driver database; or
  • create a fresh named instance when the work specifically tests first-run, claim, or empty-server behavior.

Prerequisites

The pinned source tree declares Bun 1.3.11 and Node 24.x. Install Docker, clone Nautilo, and install workspace dependencies:

cd <nautilo-checkout>
bun install

Electron preparation may also require the Rust toolchain because the desktop build includes a native apply-patch helper. The launcher prints the specific remediation when that prerequisite is missing.

Path A: reuse a claimed default instance

Use this path when default already has a known owner, a login you can retrieve, and a working model provider.

cd <nautilo-worktree>
bun run server:stop --instance default
NAUTILO_PROFILE=source-default \
  bun run dev-stack --instance default --i-know-what-i-am-doing --electron

--electron launches the desktop after the Server passes runtime acceptance. Omit it for Server/API/browser work. The separate NAUTILO_PROFILE prevents the source-built Electron process from sharing auth, cache, and its single-instance lock with an installed Nautilo desktop. It does not isolate the Server database.

Default retains data and may run migrations

dev-stack starts infrastructure and may apply committed Drizzle migrations to the retained default database. Reconcile competing migration histories before moving a long-lived default instance between branches.

The --i-know-what-i-am-doing flag is required when a feature worktree explicitly targets default. It confirms the target; it does not back up the database or make an incompatible migration safe.

Path B: clone default into an isolated named instance

This is the normal isolated path when default already represents a usable Nautilo installation. The target must not exist.

cd <nautilo-worktree>
NAUTILO_PROFILE=feature-lab \
  bun run dev-stack \
  --instance feature-lab \
  --clone-default \
  --electron

The command captures or reuses a verified, bounded seed from the canonical default instance; creates separate databases, Compose resources, ports, files, and instance identity; materializes the retained users, authentication state, provider configuration, artifacts, and durable data into that target; then runs the ordinary Server and client acceptance path. It does not delete or compact the source.

Use --refresh-clone-seed only when the existing verified seed is not fresh enough for the test. A populated target is never overwritten: reuse that exact instance or deliberately delete that named scratch environment before trying to clone it again.

A clone is isolated, not empty

A clone is the wrong test for first-run setup or an unclaimed Server. It also starts from the source database's migration lineage; inspect schema changes before using either the clone or the retained default for migration work.

Path C: create and claim a fresh named instance

Do not launch Electron first. Bring up the isolated Server, claim it, record the login, configure a model route, then launch the UI.

Choose a lowercase instance ID:

bun run dev-stack --instance feature-lab

Keep that terminal running. In another terminal, create a deploy input outside the checkout, for example ~/.config/nautilo/feature-lab.toml:

schemaVersion = 1

[admin]
handle = "operator"
displayName = "Local Operator"
password = { fromEnv = "NAUTILO_DEV_ADMIN_PASSWORD" }
pin = { fromEnv = "NAUTILO_DEV_ADMIN_PIN" }
forcePasswordChangeOnFirstSignIn = false

[[providers]]
key = "ANTHROPIC_API_KEY"
value = { fromEnv = "ANTHROPIC_API_KEY" }

[genie]
mode = "randomize"

Keep only providers you actually use. Put every referenced value in a mode-0600 operator secrets file or export it in the setup shell. Never let an automation invent a password and then discard the only copy.

For example, ~/.config/nautilo/secrets.env can contain:

NAUTILO_DEV_ADMIN_PASSWORD=<store-a-real-development-password-here>
NAUTILO_DEV_ADMIN_PIN=<store-a-real-development-pin-here>
ANTHROPIC_API_KEY=<your-provider-key>
chmod 600 ~/.config/nautilo/feature-lab.toml
chmod 600 ~/.config/nautilo/secrets.env

bun run dev:setup \
  --instance feature-lab \
  --config ~/.config/nautilo/feature-lab.toml \
  --secrets-file ~/.config/nautilo/secrets.env

bun run dev:setup-status --instance feature-lab

dev:setup-status exits successfully only when setupState is ready. Store any one-time recovery codes printed during claim before continuing.

Stop the first dev-stack with Ctrl-C, then start the same instance with the desktop:

NAUTILO_PROFILE=feature-lab bun run dev-stack --instance feature-lab --electron

Sign in with the credentials you deliberately stored and complete one model-backed turn. That is the first usable-development checkpoint.

What dev-stack proves

Before printing ready, current source checks:

  • Server health;
  • the target instance identity;
  • the Workbench SPA;
  • the application database roles and a direct PostgreSQL connection; and
  • Logto OIDC discovery.

It does not prove that setup state is ready, that the owner can sign in, or that a model-backed turn succeeds. Those are separate acceptance steps above.

Run more than one desktop against one Server

Start the named Server without --electron, then launch distinct desktop profiles in separate terminals:

NAUTILO_INSTANCE_ID=feature-lab NAUTILO_PROFILE=operator-one bun run desktop
NAUTILO_INSTANCE_ID=feature-lab NAUTILO_PROFILE=operator-two bun run desktop

Profile slugs use lowercase letters, digits, underscores, and hyphens. Nautilo isolates Electron state by the (instance, profile) tuple, so the clients can hold different authenticated users while sharing the same Server.

Each command opens that profile's first-run connection picker; the launcher does not silently pair the desktop. In each window, enter the named Server URL printed by dev-stack, verify that the picker reports the intended Server and setup state, then sign in as a different user. The multi-user proof begins only after both profiles are explicitly paired with the same URL.

Stop without deleting data

Press Ctrl-C in the dev-stack terminal. It stops the Server and Electron children it started but leaves instance infrastructure and data intact.

Use instance deletion only when you deliberately intend to destroy that named environment. Stopping a process and deleting an instance are different operations.