Contents

Self-Host Yoke

Run the Yoke API server on your own host: one docker compose bundle carrying the published server image plus a Postgres 17 database. Your data stays on hardware you control; engineers point their CLIs at your server instead of the hosted platform.

Quickstart

Install Docker with Compose on the server host first, then verify docker compose version succeeds. Docker Desktop includes Compose; Docker Engine installations need the Compose plugin. Init checks this prerequisite before writing or changing the bundle. If Docker or Compose is missing, install Docker (with Compose) and run yoke self-host init again; Yoke does not install them for you. On the server host:

# 1. Install the CLI (also how engineer machines install it later).
curl -fsSL https://stage.upyoke.com/install | sh

# 2. Materialize the compose bundle. Writes docker-compose.yml, .env
#    (carrying your name: first boot creates the first admin as you),
#    and generated database credentials as owner-only secret files —
#    the generated password is never printed. A marked block in
#    .gitignore protects .env and secrets/ without replacing your rules.
yoke self-host init --admin-name "Your Name" --start
cd "${YOKE_MACHINE_HOME:-$HOME/.yoke}/yoke-server"

# 3. First boot writes the reusable administrator token to an owner-only
#    file. The log names the path and never carries the token itself.
docker compose logs core

# 4. Attach your CLI (verifies the server and token before persisting
#    anything). Keep the owner-only token file: it stays valid until you
#    revoke it, and rerunning `yoke setup` on this host reconnects with it.
yoke connect http://127.0.0.1:8765 --token-stdin < secrets/first-boot-admin-token

# 5. Confirm the machine is wired up. This fails if the server is not
#    answering, so a green run means the whole path works.
yoke status

The host command opens each mode 0600 input as its non-root operator and streams it on stdin through Docker exec. The container drops to its runtime user and writes private tmpfs copies; no host file is mounted or chowned and no credential is placed in an environment variable. The admin token returns over that same private handoff, outside Docker logs; the host atomically writes its owner-only token file and acknowledges durable storage before token creation commits. A failed handoff refuses with a reason and retry command. Start/restart an existing bundle with yoke self-host init --dir PATH --protect-existing --start. This refreshes the packaged Compose template, preserving .env, database credentials, and the Postgres volume. Keep runtime settings in .env; reapply any custom Compose changes after a template refresh. Automatic container restart is disabled: after a host/daemon restart, run that host command again so it can reopen the protected inputs. Raw Compose starts alone cannot supply the handoff. Native Ubuntu rootful Docker is exercised by the manual bootstrap probe; rootless Docker and Fedora/SELinux remain unproved. A new bundle requires --admin-name, written to .env as YOKE_ADMIN_NAME; first boot refuses to birth a universe without it and names the fix (set it in .env, then restart). The bundle defaults to ~/.yoke/yoke-server (YOKE_MACHINE_HOME/yoke-server when set), outside projects. yoke self-host init takes --dir, --port, and --image overrides. By default it resolves the installed CLI's immutable release manifest and writes that release's exact <repository>:<BASE.YYYYMMDDHHMM> image to .env. The repository defaults to ghcr.io/upyoke/yoke-server; set settings.server_image_repository in machine config, or choose Image repository in the self-host onboarding preview, to use a fork, mirror, or private registry. Enter a lowercase registry/repository without a tag or digest; it must carry the same immutable release version tags. Every fresh bundle therefore starts with matching CLI and server versions, and a later container restart keeps the same server. --image remains an explicit operator override. Generated credentials stay in host-owned files under secrets/ rather than .env, whose values Compose $-interpolates. Refresh older bundles while preserving .env and database credentials:

yoke self-host init --dir /path/to/yoke-server --protect-existing

The command preserves operator-authored .gitignore rules and reports that credentials were not regenerated. The bundle and secrets/ must be real operator-owned directories; secrets/ must be mode 0700. It refuses symlinked paths and secrets already tracked by Git. Remove reported paths from the index, rotate any exposed credential, and retry.

Mac runtime and private-address preparation

On supported macOS choose Docker Desktop or Colima with Docker CLI and Compose. Check vendor OS/architecture support; current Desktop does not support older macOS such as Monterey. Follow Colima/Lima's QEMU instructions for those hosts. Install Compose as a Docker CLI plugin; docker compose version must report v2. Start your chosen runtime yourself: open Desktop and finish its approved first run, or run colima start in the owning user's Colima environment/context. Require docker info to succeed before Start; Yoke installs or starts no runtime. Setup's interactive and agent modes share this check and explain what to repair. Loopback hosting needs no Colima host-address change. For a numeric Mac LAN or Tailscale --listen address, Colima must forward that HOST address into its VM. Read the installed colima start --help and vendor hostAddresses configuration. With permission to stop that runtime, save its profile and ensure no unrelated workload is running, then run colima stop followed by colima start --network-host-addresses=true --network-address=false. Preserve every other profile setting. The VM address is not the Mac address. Verify a container published only on the chosen Mac private IP from another machine; separately require a loopback-only control to remain unreachable there. An unsupported flag or failed bind needs runtime configuration repair before retrying the existing bundle with --protect-existing --start; widening to 0.0.0.0 is not the repair. These Colima settings do not apply to Docker Desktop.

Move an existing universe here

Point the import at a fresh or replaceable bundle, keep core stopped, and protect the archive as private control-plane data:

yoke self-host init --dir /path/to/yoke-server
chmod 600 ~/Downloads/acme-universe-20260714T120000Z.tar
yoke self-host import ~/Downloads/acme-universe-20260714T120000Z.tar \
  --dir /path/to/yoke-server

The tar carries the database dump and freeze receipt (see Universe portability); that receipt supplies checksum verification. Type replace at the prompt, or pass --yes for a non-interactive replacement. The command requires Docker with Compose, validates the bundle, and refuses while core runs. The archive must be a current-owner, single-link regular file with no group/world access. Compose starts only the database and streams the archive to a one-off process in the pinned image; it never bind-mounts the host archive. Uploaded DDL never runs. Yoke creates the trusted destination schema, validates the bounded archive, and restores approved data and sequences in one transaction; retry replaces a failed or interrupted attempt. Archives contain private records and hashed credentials; capability-secret data is excluded. Keep them owner-only and rotate secrets when custody changes. Restore revokes imported API tokens and browser sessions, re-credentials the archive's org-admin person (an archive with none refuses), and mints one replacement token. Save its one-time success output, then run the printed yoke self-host init --protect-existing --start and yoke connect steps. If the restore reported success but its one-time result was lost before you could save it, mint a recovery credential while core remains stopped:

cd /path/to/yoke-server
yoke self-host import --dir . --recover-credential

Save that command's raw_token, then start the service. Recovery atomically revokes every prior import/recovery credential before minting its replacement, so it is safe to repeat if another one-time result is lost.

Export over the server connection

After yoke connect selects this self-host server, an org administrator can stream a portable archive without acquiring its database DSN:

yoke universe export --out ~/backups/

The CLI sends its bearer token only to the configured server, refuses redirects, requires the archive media type, enforces the portability size and time bounds, and publishes the owner-only destination file atomically. The generated Compose bundle marks the runtime with YOKE_SERVER_MODE=self-host; without that explicit marker the core endpoint is hidden. Hosted Platform tenants continue through Platform's fleet-coordinated download route instead of this self-host boundary.

Networking

By default the API publishes on loopback only (127.0.0.1:8765). To serve your network, pass --listen ADDRESS at init, for example yoke self-host init --admin-name "Your Name" --listen 100.101.102.103 --start. This writes YOKE_API_PUBLISH=100.101.102.103:8765 in .env; teammates run yoke connect http://100.101.102.103:8765. Use a LAN or Tailscale IP; --listen 0.0.0.0 binds all IPv4 interfaces, so teammates substitute the server's reachable private IP. IPv6 binds are supported too. Private HTTP permits 10/8, 172.16/12, 192.168/16, 100.64/10, fd00::/8 and *.ts.net; public HTTP is refused with HTTPS recovery. Each connect warns that the machine token travels unencrypted: safe on Tailscale or a VPN; use TLS on a shared LAN. Existing bundles keep their .env with --protect-existing; edit YOKE_API_PUBLISH there and restart to change it. Docker bypasses ufw/firewalld for published ports; bind to 127.0.0.1 behind a TLS reverse proxy, or restrict access upstream. yoke self-host init warns when the configured publish address is beyond loopback, including on --protect-existing --start; the warning never blocks setup or startup. Set YOKE_API_TRUSTED_PROXIES in .env to the proxy IPs/CIDRs seen by core; restart with yoke self-host init --dir PATH --protect-existing --start. See proxy forwarding for headers and trust.

Engineer machines

See People and machines to connect engineers and approve your own machines.

Restore admin access

The host stays connected after setup. If this machine loses its connection, rerun yoke setup on the host and choose Host a server here: it finds the existing bundle, restarts it, and reconnects with secrets/first-boot-admin-token without asking anything new. If that credential is lost or revoked, the first-boot and recovery bootstrap is available on the server host:

docker compose exec --user yoke core python3 -m yoke_core.domain.api_tokens_cli bootstrap-admin --actor-name "Your name" --name recovery

Save the one-time token and reconnect with yoke connect <server> --token-stdin. Once connected, use personal token controls to revoke the lost token and manage your own tokens.

Web dashboard and browser sign-in

See Browser sign-in for token links, company sign-in and proxy configuration.

GitHub App server automation

Use a dedicated operator-owned App for server automation. GitHub App setup covers private-key ingress and runtime settings.

Upgrades

Running bundles stay on their exact image pin until you deliberately advance the pair. Upgrades use the configured image repository. From any directory, run:

yoke self-host upgrade --dir /path/to/yoke-server

The command performs a read-only preflight and shows the current image, target release, exact target image, and ordered actions before asking you to type upgrade. It then installs that release's CLI through the public installer channel, atomically rewrites YOKE_SERVER_IMAGE, runs docker compose pull core, refreshes the Compose template, and restarts through the private host handoff. Use --yes only when an automated run has already accepted that same plan.

An active source-checkout CLI refuses this command before preview or install, just as yoke update does. Update the checkout with git, then rebuild its source server with yoke core upgrade --from-checkout /path/to/yoke --build. Checkout builds pass Git HEAD and the checkout's setuptools-scm version into the image, so installed wheels pass version and migration-readiness checks. The refusal preserves the CLI install, bundle image pin, and running server.

Failures name the stage and exact retry. The pin is unchanged when CLI install fails; after the CLI and pin advance, a pull or restart failure leaves both durable identities on the target and prints the two Compose recovery commands. On every boot the entrypoint converges additive schema before serving; data-transforming changes still use Yoke's governed migration runner. Foreign keys onto environments.id match its live type so a universe still on text keys reaches the ordered history that converts them. The boot also declares a bounded idle_in_transaction_session_timeout for its own session and records it as the role's database default; a managed Postgres whose role cannot alter its own defaults degrades with an application_role_default_not_persisted line on stderr rather than refusing to serve. Confirm the running source identity through the build field on GET /v1/health.

Run a server from an edited Yoke checkout

Choosing Edit Yoke source in the wizard always activates that checkout for the local CLI and harness. When the destination is guided self-hosting on the same machine, the wizard also uses the existing local-core --from-checkout PATH --build path so the private server image contains that checkout's code. Connecting the edited client to an already-running team server or stage.upyoke.com does not replace its image; the remote server keeps its deployed build.

The ordinary published-image bundle remains pinned to YOKE_SERVER_IMAGE. Building a custom image from the repository Dockerfile and selecting that private/local image is still supported, but editing a checkout does not silently repoint an unrelated remote bundle.

Take it back off

For full machine removal, use yoke uninstall. yoke self-host teardown removes the server install. It always stops and removes the stack; everything further is opt-in and named for what it destroys. Run it from anywhere; it defaults to the machine-home bundle. Name another bundle with --dir PATH:

yoke self-host teardown                       # stop the stack; keep the data
yoke self-host teardown --remove-images \
  --remove-bundle --destroy-universe          # remove everything, prompts first

Without --destroy-universe the pgdata volume survives, so yoke self-host init --protect-existing --start brings the same universe back. With it, the volume and every item, event, and credential in it are gone; the command asks for consent unless you pass --yes. --remove-images removes the images this bundle uses and reports any another container still needs. --remove-bundle deletes the files Yoke wrote, secrets/ and the admin token file included, and reports anything else in the directory rather than deleting it.

Teardown atomically retires every machine connection pointing at this bundle's server before stopping it, including each connection's unshared token file, so no dead authority or credential is left behind. Pass --keep-connection to leave all of them, or --activate ENV to name a connection for another server that takes over as this machine's authority. Idle machine-config lock files created by retirement are removed as part of teardown. Single-connection retirement remains available on its own as yoke connection remove ENV [--activate ENV]; removing your last connection leaves the machine unconfigured, which yoke status then reports.

You own the operations

  • Uptime is yours. Automatic restart is disabled. After a failure or host restart, use yoke self-host init --dir PATH --protect-existing --start to reopen the private inputs. You own monitoring and capacity.
  • Backups are yours. All state lives in the pgdata volume; use yoke universe export for portable archives and retain regular Postgres or volume snapshots for infrastructure-level recovery before upgrades and on a schedule.
  • TLS is yours. Use TLS for public access or shared LANs and declare proxy peers in YOKE_API_TRUSTED_PROXIES; publish the API only where intended.

Optional GeoIP context

Mount unpacked DB-IP Lite City and ASN .mmdb files read-only and set YOKE_GEOIP_CITY_DB and YOKE_GEOIP_ASN_DB to their container paths. Update the files monthly; missing files leave collection working without geo fields. The trusted client IP is still stored. Follow the privacy and DB-IP credit requirements.

Install and run a server · Yoke