Skip to content

Manual single-node bring-up

orlop dev up (quickstart) does everything on this page for you. Do it by hand to see the moving parts, to customize ports or storage, or to model the sequence for your own orchestration.

You need the orlop, orlop-control, and orlop-server binaries on your PATH (install). Confirm the host can mount and the three ports are free — the same preflight dev up runs:

Terminal window
orlop doctor --dev # mount support + writable cache + ports 8080/7878/8443 free

The five steps are exactly what dev up automates, in order: control plane → register server → data-plane server → enroll token → mount.

Embedded SQLite needs nothing external: point DATABASE_URL at a file and the schema is created on first open. The CA initializes itself on first boot and writes its root and tenant keys under ORLOP_SECRETS_DIR (created if missing).

Terminal window
export DATABASE_URL="sqlite:./orlop.db" # schema applied on first open
export ORLOP_SECRETS_DIR=./orlop-secrets # CA keys live here
# the token, trust domain, and FQDN must match the data-plane server (step 3)
export ORLOP_CONTROL_PLANE_TOKEN=$(openssl rand -hex 16)
export ORLOP_TRUST_DOMAIN=demo.example
export ORLOP_SERVER_FQDN=localhost
PORT=8080 orlop-control &
# ready when: GET http://localhost:8080/healthz → 200

/agent/enroll places each agent on a server from the pool, so the one local server must be registered before any agent enrolls — an empty pool returns 503. server register writes straight to the database (it reads DATABASE_URL), so the running control plane needs no restart.

Terminal window
orlop-control server register \
--data-addr localhost:8443 \
--ops-addr localhost:7878 \
--total-bytes $((10 * 1024 * 1024 * 1024)) # pool capacity (also the default)

--data-addr is where agents dial; --ops-addr is where the control plane dials. Both stay localhost, so one self-provisioned cert covers both. Re-running is idempotent on --data-addr.

server.yaml
tenant:
id: a_demo # bootstrap tenant; more register at enroll
name: demo agent disk
store: { type: local, root: ./orlop-data/objects }
routes: { type: sqlite, path: ./orlop-data/routes.db }
server:
ops_bind: ":7878" # bare :port — see the note below
data_bind: ":8443"
tls:
self_provision: true # fetches its cert and the client CA from the control plane
control_url: http://localhost:8080
fqdn: localhost
trust_domain: demo.example
tenants_root: ./orlop-data/tenants
quota: { enforce: false }

Bind to :8443, not 127.0.0.1:8443. The mount client dials localhost, which can resolve to IPv6 ::1; a 127.0.0.1-only listener refuses that. The bare :port form listens on both stacks while the cert SAN stays localhost.

Terminal window
mkdir -p orlop-data/objects orlop-data/tenants
# the service token must equal ORLOP_CONTROL_PLANE_TOKEN — it authenticates
# the cert self-provisioning request to the control plane
ORLOP_SERVICE_TOKEN="$ORLOP_CONTROL_PLANE_TOKEN" \
orlop-server -config server.yaml &
# ready when: "orlop-server data-plane TCP listening with mTLS" bind=":8443"
Terminal window
orlop-control token issue --agent demo

It provisions the agent’s disk and prints a ready-to-paste block. The token is single-use and short-lived (10m), so mount promptly:

mount it with:
export ORLOP_AGENT_ID=demo
export ORLOP_MOUNT_POINT=./agent-disk
export ORLOP_CONTROL_PLANE=http://localhost:8080
export ORLOP_ENROLL_TOKEN=<token>
orlop mount --from-env

Paste those exports, then mount. --from-env trades the enroll token for a short-lived client cert via /agent/enroll and mounts over the mTLS data path:

Terminal window
# ... paste the export block above, then:
orlop mount --from-env &
# ready when: "mount verified at ./agent-disk"
Terminal window
echo "hello from a durable agent disk" > ./agent-disk/hello.txt
orlop unmount ./agent-disk # the foreground mount exits cleanly; the dir goes empty
ls ./agent-disk # empty
# the enroll token is single-use — the first mount spent it — so mint a fresh
# one and re-paste its block before remounting
orlop-control token issue --agent demo
# ... paste the new export block, then:
orlop mount --from-env &
cat ./agent-disk/hello.txt # → hello from a durable agent disk

The file survived because it lives in the data-plane server’s store (./orlop-data/objects), not in the mount point.

A single-node bring-up only breaks where the two halves disagree. Keep these in sync:

control plane data-plane server why
ORLOP_CONTROL_PLANE_TOKEN ORLOP_SERVICE_TOKEN authenticates cert self-provisioning
ORLOP_SERVER_FQDN tls.fqdn the server cert SAN agents validate
ORLOP_TRUST_DOMAIN tls.trust_domain SPIFFE trust domain on every cert
server register --data-addr host tls.fqdn agents dial the name in the cert
Terminal window
kill %1 %2 %3 2>/dev/null # orlop mount, orlop-server, orlop-control
rm -f ./orlop.db* # SQLite database and its WAL sidecars
rm -rf ./orlop-secrets ./orlop-data ./agent-disk

This is a single-node developer bring-up. For Postgres and multiple control-plane replicas, quota enforcement, or JuiceFS-backed storage, see database-backends.md and control-plane-runbook.md.