Skip to content

Getting Started

This guide walks you from nothing to a managed fleet, moving across three machines: your workstation, the orchestrator host, and a machine you want to manage. The configuration files and systemd units shown below are the same templates published with each release (also listed on the Deployment templates reference page).

  • Installing the right Ordo binaries on each machine
  • Creating the first operator identity and bootstrapping an orchestrator
  • Running the orchestrator and an agent as systemd services
  • Approving and tagging an agent from the operator CLI
  • Opening the web UI
  • A machine to run the orchestrator, and one or more machines to run agents. These can be the same machine while you are exploring — with one caveat: Ordo does not support an agent managing the orchestrator’s own host. Other state on that machine can still be managed normally.
  • Network connectivity from each agent to the orchestrator on the agent port (4747 by default).

On the machine you will operate Ordo from, install the client tools (ordo and ordo-state):

Terminal window
curl -fsSL https://getordo.dev/install.sh | sh

Then generate an operator keypair:

Terminal window
ordo operator init

This prints your public key (hex) and operator ID. The public key bootstraps the orchestrator in the next step; the private key stays in the operator data directory and is never shared. If you lose the public key, print it again with:

Terminal window
ordo operator whoami

On the orchestrator host, install the orchestrator:

Terminal window
curl -fsSL https://getordo.dev/install.sh | sh -s -- ordo-orchestrator

Confirm where it landed — the systemd unit below references /usr/local/bin:

Terminal window
which ordo-orchestrator

Create the configuration directory and an orchestrator.yaml. Start from the template below — every option shows its default, commented out unless it must be set. Paste your public key from step 1 into bootstrap_key.

Terminal window
sudo mkdir -p /etc/ordo
/etc/ordo/orchestrator.yaml
# Ordo orchestrator configuration — example.
#
# Every option is shown with its default value. Commented lines are defaults you
# can leave alone; uncomment and change only what you need. The uncommented
# settings below are the ones a real deployment must or should set.
#
# Schema (editor autocompletion / CI validation):
# https://getordo.dev/schemas/orchestrator-config/v1.json
# ── Must be set on first run ────────────────────────────────────────────────
# The bootstrap operator. Both are required the first time the orchestrator
# starts (when no operators exist yet) and ignored on every start afterwards.
# Get the public key from `ordo operator init` (or `ordo operator whoami`).
bootstrap_key: "PASTE_OPERATOR_PUBLIC_KEY_HEX"
# 1-64 letters/digits/-/_, must start with a letter.
bootstrap_username: "CHOOSE_A_USERNAME"
# ── Recommended ─────────────────────────────────────────────────────────────
# Interface the management API binds. The default (127.0.0.1) is reachable only
# from the orchestrator host itself — fine for a single-machine/dev setup. Bind
# all interfaces so the web UI and CLI work from other machines; choose a
# specific address for a more complex network.
api_host: "0.0.0.0"
# ── Networking (defaults shown) ─────────────────────────────────────────────
# TCP port on which the orchestrator accepts agent connections.
# agent_port: 4747
# HTTP port on which the management API is served.
# api_port: 4748
# Address advertised to agents during discovery; empty = auto-detect.
# advertised_host: ""
# ── TLS (defaults shown) ────────────────────────────────────────────────────
# TLS is enabled by default; a self-signed certificate is generated on first
# start and pinned by clients on first connection (trust-on-first-use).
# tls_enabled: true
# Provide both to use your own PEM certificate instead of the self-signed one.
# tls_cert: "/etc/ordo/tls/cert.pem"
# tls_key: "/etc/ordo/tls/key.pem"
# Assert that a TLS-terminating reverse proxy fronts the API. Only relevant when
# tls_enabled is false; permits secret writes over the (proxied) plaintext hop.
# trust_proxy_tls: false
# ── Capacity and sessions (defaults shown) ──────────────────────────────────
# Maximum number of simultaneously pending agents awaiting approval.
# pending_capacity: 100
# Seconds a web UI / API session token remains valid.
# session_token_expiry_secs: 3600
# ── Metrics (defaults shown) ────────────────────────────────────────────────
# metrics:
# poll_interval: 60 # seconds between polling each agent for metrics
# retention: 86400 # seconds to retain metric samples (24h)
# ── Audit log (defaults shown) ──────────────────────────────────────────────
# audit:
# retention_days: 90 # 0 disables automatic pruning
# ── Drift detection (defaults shown) ────────────────────────────────────────
# drift:
# enabled: true
# interval: 3600 # seconds between drift checks per agent
# max_concurrent: 8 # max in-flight drift checks across all agents
# skip_recently_applied: 300 # suppress checks for N seconds after an apply
# retention_days: 30 # how long to retain drift records

Install the unit file, then enable and start it:

/etc/systemd/system/ordo-orchestrator.service
[Unit]
Description=Ordo orchestrator
Documentation=https://docs.getordo.dev
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/ordo-orchestrator --config /etc/ordo/orchestrator.yaml --data-dir /var/lib/ordo-orchestrator
Restart=on-failure
RestartSec=5
# Isolated transient system user; StateDirectory gives a persistent, owned
# /var/lib/ordo-orchestrator for the identity key and the redb store. The
# orchestrator only talks over the network and reads its config, so it can run
# fully sandboxed (unlike the agent, which manages the host).
DynamicUser=yes
StateDirectory=ordo-orchestrator
StateDirectoryMode=0750
# Hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=yes
LockPersonality=yes
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
[Install]
WantedBy=multi-user.target
Terminal window
sudo systemctl daemon-reload
sudo systemctl enable --now ordo-orchestrator
journalctl -u ordo-orchestrator -f

A first-run failure is almost always a missing or invalid bootstrap_key / bootstrap_username.

On the machine you want to manage, install the agent:

Terminal window
curl -fsSL https://getordo.dev/install.sh | sh -s -- ordo-agent

Create an agent.yaml. The defaults are sensible — the file is mostly a place to opt into the remote terminal later:

/etc/ordo/agent.yaml
# Ordo agent configuration — example.
#
# Every option is shown with its default value, all commented out: the defaults
# are sensible, so an agent runs with no configuration at all. Uncomment only
# what you want to change. The agent hot-reloads this file when started with
# `--config`, so changes apply without a restart.
#
# The orchestrator address is NOT set here — it is passed to the `connect`
# subcommand (e.g. `ordo-agent connect 192.168.40.243:4747`).
#
# Schema (editor autocompletion / CI validation):
# https://getordo.dev/schemas/agent-config/v1.json
# ── Remote terminal (defaults shown) ────────────────────────────────────────
# Lets operators open interactive PTY sessions on this machine through the
# orchestrator. Disabled by default.
# remote_terminal:
# enabled: false
# shell: "" # empty = platform default (/bin/bash, powershell.exe)
# allow_operator_shell_override: false # let the operator choose the shell per session
# max_sessions: 100 # max concurrent terminal sessions
# ── Metrics (defaults shown) ────────────────────────────────────────────────
# System health reporting (disk, memory, CPU) to the orchestrator.
# metrics:
# enabled: true
# collection_interval: 30 # seconds between samples

Install the agent unit, replacing ORCHESTRATOR_HOST with your orchestrator’s address:

/etc/systemd/system/ordo-agent.service
[Unit]
Description=Ordo agent
Documentation=https://docs.getordo.dev
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
# Top-level options (--config, --data-dir) come BEFORE the `connect` subcommand;
# replace the address with your orchestrator's host:agent_port.
ExecStart=/usr/local/bin/ordo-agent --config /etc/ordo/agent.yaml --data-dir /var/lib/ordo-agent connect ORCHESTRATOR_HOST:4747
Restart=on-failure
RestartSec=5
# The agent manages this machine — it writes files and controls services as
# declared state requires — so it runs as root WITHOUT the filesystem sandboxing
# used for the orchestrator. ProtectSystem/ProtectHome/DynamicUser would stop it
# doing its job. StateDirectory persists the agent's identity and pinned
# orchestrator key.
User=root
StateDirectory=ordo-agent
[Install]
WantedBy=multi-user.target
Terminal window
sudo systemctl daemon-reload
sudo systemctl enable --now ordo-agent
journalctl -u ordo-agent -f

On first connection the agent pins the orchestrator’s key and enters the Pending state on the orchestrator, awaiting your approval.

Back on your workstation, list agents, approve the pending one, and tag it so state can target it later:

Terminal window
ordo agents
ordo agents approve <node-id> --name client-01
ordo agents tag client-01 group=home

Create a connection profile pointing at the orchestrator. The first profile you create automatically becomes the default, so the CLI uses it with no further configuration:

Terminal window
ordo profile create home --host <orchestrator-host>

Then mint a web UI session and open the printed URL:

Terminal window
ordo web-login

It prints a login token and a https://<orchestrator-host>:4748/ui/auth/callback?token=… URL — open it in a browser to reach the UI authenticated as your operator.

Your fleet is connected and managed. From here you can author a state module and apply it to the agent’s tags — see the Reference for the state file format and CLI, and the Deployment templates for the raw configuration and unit files.