Quickstart#
Ten minutes from install to a managed fleet. Commands below assume an
installed serverctl; from a source checkout use ./scripts/serverctl.
1. Check your environment#
serverctl doctor
Fix anything not ok before continuing (Installation,
Platforms).
2. Bring in the servers you already have#
serverctl server import # preview: what would be imported
serverctl server import --apply # copy literal Host aliases into the manager
The importer follows Include directives and resolves each alias with
ssh -G. Wildcard patterns are reported but not imported. Your original
~/.ssh/config is read, never written.
3. Store a credential#
serverctl credential add-password prod-password # hidden local prompt, stored in OS vault
serverctl credential add-key deploy-key --key-path ~/.ssh/id_ed25519 --store-passphrase
serverctl credential add-agent just-use-my-agent # ssh-agent / OpenSSH defaults
There is deliberately no CLI command that prints a stored secret. Revealing one requires the web UI plus passkey / master-password re-auth.
4. Add a server and attach the credential#
serverctl server add web1 \
--hostname web1.example.com --username deploy \
--credential prod-password --notes "primary web node"
Or do both steps at once — this creates the password credential through the same hidden prompt and attaches it in a single command:
serverctl server add web1 \
--hostname web1.example.com --username deploy \
--password-credential prod-password
Already created the server without a credential? serverctl ui --credential
web1 opens the browser directly on web1's credential form.
Note that add only creates — it refuses an alias that already exists. To
change anything later (including the attached credential), use
serverctl server edit web1 ...; to replace a stored password, use
serverctl credential edit prod-password --replace-secret.
Behind a bastion? Model the hop as its own alias and chain it:
serverctl server add bastion --hostname bastion.example.com --username jump
serverctl server add db1 --hostname 10.0.2.7 --username dba --proxy-jump bastion
5. Test, connect, execute#
serverctl server test web1 # latency + classified error code
serverctl connect web1 # interactive shell
serverctl exec web1 -- uptime # one command, args passed safely
serverctl exec web1 --shell -- 'df -h | sort -k5 -r | head' # pipelines need --shell
Stream a file or script:
cat deploy.sh | serverctl exec web1 --stdin -- sh -s
cat build.tar.gz | serverctl exec web1 --stdin-binary -- sh -c 'cat > /tmp/build.tar.gz'
Run several commands without re-authenticating each time (macOS/Linux):
serverctl exec web1 --reuse 300 -- uptime # keeps the connection alive 300 s
6. Optional: attach environment-specific Agent Skills#
Discover local Skills, register one, and bind it only to the Hosts where its guidance applies:
serverctl skill discover --json
serverctl skill add ~/.agents/skills/web-operations --server web1 --json
serverctl skill resolve web1 --json
Replace the example path with a candidate returned by discover. Discovery is
local and read-only. A binding makes a Skill eligible for that Host; normal
Skill trigger rules still decide whether it matches the task, and the binding
does not authorize remote changes. See
AI agents.
7. Optional: the web UI#
serverctl ui
Opens the local management interface with a one-time tokenized URL — add/edit servers and credentials, discover and register Skills in the Skill Library, manage a Host's Skill assignments, run tests, import config, and reveal a stored secret after re-authentication. See Web UI.
8. Plain ssh still works#
Every managed alias is rendered into ~/.ssh/ssh-server-manager.conf, which
Includes your original ~/.ssh/config at the end — so managed values win
while your existing defaults keep applying. To use managed aliases with plain
OpenSSH tools, point them at the rendered file:
ssh -F ~/.ssh/ssh-server-manager.conf web1
scp -F ~/.ssh/ssh-server-manager.conf ./file web1:/tmp/
rsync -e 'ssh -F ~/.ssh/ssh-server-manager.conf' -avz ./dir/ web1:/srv/dir/
Do not add Include ~/.ssh/ssh-server-manager.conf to your original
config — that would create an Include cycle, and serverctl will refuse to
render. Password/passphrase injection only happens through serverctl;
plain ssh prompts as usual.