Skip to content

Commands

managed init

Initialize a new managed project. Walks you through an interactive wizard to set up cloud providers, validate credentials, and pick defaults.

bash
managed init

The wizard:

  1. Asks which cloud providers to configure (DigitalOcean, Hetzner, Linode, OVH)
  2. Prompts for each provider's credentials and verifies them against the API
  3. Lets you pick a default region and server size
  4. Writes managed.yaml (with ${VAR} references) and managed.env (with actual secrets)
  5. Adds managed.env to .gitignore if you're in a git repo

After init, add services to managed.yaml and run managed apply.

managed plan

Preview what managed apply would change, without touching anything.

bash
managed plan

Reads managed.yaml and your saved state only, it never contacts a cloud provider and never changes anything. Each line shows an action (create, install, configure, firewall, schedule, destroy, remove, keep) and a plain-language reason, so you can sanity-check a config edit before applying it. Run it any time.

It's an intended diff (what your config asks for), not a drift check, the verify step during apply is what catches a service that has actually drifted on a box.

managed apply

Create or update infrastructure to match your managed.yaml.

bash
managed apply

This is the main command. It runs through all apply phases in order: provision storage, create servers, install, configure, firewall, schedules, fleet placement, scale down, and reconcile removals. Every phase is idempotent, re-running skips completed steps.

It shows the plan first. Before making changes, apply prints the same plan as managed plan. If the plan would destroy any server (for example because you removed a service or reduced its replicas), it stops and asks you to confirm, type yes, or pass a flag below. Servers marked protect: true are never destroyed, even with these flags (see Configuration).

FlagDescription
--allow-destroyPermit scale-down to destroy servers that are no longer in managed.yaml (skips the prompt)
--allow-adoptPermit locking down adopted/BYOB boxes (base-os: disables root SSH login, enables the firewall)
--allow-migratePermit moving a stranded, rebuild-safe instance (e.g. a replica) to an eligible server
-f, --forceSkip the confirmation prompt (implies --allow-destroy, --allow-adopt and --allow-migrate)
--rollbackRestore the bookkeeping from before the last apply (see below)
--exposeShow generated passwords in the printed connection strings
--skip-verifySkip the SSH drift-check of existing servers
--ignore-unreachableContinue past servers that fail SSH during verify
--only <phase>Run a single phase only (network, install, firewall, configure, verify), see below
--servers <names>Limit the run to specific servers (comma-separated)

External/BYOB boxes you adopt are only locked down (root SSH login disabled, packages upgraded, firewall enabled) when you pass --allow-adopt, and that lock-down runs last, after the private network and all firewall rules are in place, so turning the firewall on can't cut off a service that was working a moment ago. Without --allow-adopt the box is added and managed, but left un-hardened with a warning. See Adopting an existing fleet.

While an apply is running, managed.yaml's state is locked (.managed/state.lock) so a second apply in the same project can't corrupt it. A stale lock from a crashed run is broken automatically.

managed apply --rollback

bash
managed apply --rollback

apply snapshots your state before each run. --rollback restores that snapshot, it rewinds managed's bookkeeping only. It does not recreate or destroy any servers; use it to undo a state change, not infrastructure changes.

managed apply --only <phase>

Run a single phase of apply instead of the whole pipeline:

bash
managed apply --only network                 # just stand up the private network
managed apply --only firewall --servers web-1  # just (re)apply firewall rules on web-1

Valid phases: network, install, firewall, configure, verify. A scoped run never locks down a box, scales anything down, or runs the destroy/adopt confirmation, it does exactly the one phase you named, and --servers narrows it to specific boxes (peer and firewall-source resolution still consider the whole fleet, so a scoped firewall run still allows the right siblings).

This is how you bring an existing fleet under management one careful step at a time. See Adopting an existing fleet.

managed firewall diff

Preview what managed would change about your servers' firewalls, without touching anything, and find out whether turning the firewall on would break something that's working today.

bash
managed firewall diff                 # check the whole fleet
managed firewall diff web-1 web-2     # check specific servers
managed firewall diff --json          # machine-readable output (used by the dashboard)

The command was previously called managed fw; fw still works as an alias.

It connects to each server read-only and reports, per box:

  • open, rules managed would add
  • remove, rules managed would take away (for example, narrowing a port that's currently open to the whole internet down to your private network)
  • would close, a port that is in use right now but that no rule would keep open. Turning the firewall on would cut it off. This is the important one.

It ends with a verdict and a matching exit code, so a script or the dashboard can gate on it:

VerdictExit codeMeaning
clean0Safe, nothing in use today would be cut off
warn2Allowed, but review the changes (e.g. a public port is being tightened)
blocked3Not safe yet, a port in use would be closed; fix it first

Example:

Firewall preview
  web-1 (firewall currently off)
    ✓ open    from 10.100.0.5 to any port 4949 proto tcp
    ! remove  3306/tcp
    ✗ port 13337 would be closed, it's in use right now

  ✗ Not safe yet, turning on the firewall would close a port that's in use
    hint: Run 'managed firewall import <server>' to keep those ports open, then check again

Run this before you let managed take over a server's firewall (the --allow-adopt lock-down). It's the safety check that answers "is it safe to flip the firewall on yet?".

managed firewall import

Look at the ports a server is using right now and suggest rules to keep them open, so turning managed's firewall on doesn't cut them off. This is the fix for a blocked result from managed firewall diff. It reads the server read-only and changes nothing.

bash
managed firewall import crawler-1            # suggest rules for a server
managed firewall import crawler-1 --json     # machine-readable (used by the dashboard)

It prints a firewall.allow block you can paste under the server in managed.yaml:

  servers:
    crawler-1:
      firewall:
        allow:
          - port: 13337
            from: private

Each suggested rule is scoped to your private network, the safe default that keeps a port reachable from your own servers without exposing it to the internet. Change from: to public for a port that genuinely serves the internet, overlay for the encrypted mesh only, or a specific IP/CIDR. Then run managed firewall diff <server> to confirm it's clean.

The dashboard's Adopt wizard does this for you with a one-click "keep these ports open".

managed status

Show the current state of all servers and services.

bash
managed status

Displays server names, IPs (both private and public), status, uptime, and generated credentials.

managed refresh

Check every server against its cloud provider and update what changed.

bash
managed refresh           # report and save what changed
managed refresh --prune   # also remove servers that are gone
managed refresh --json    # machine-readable output

managed plan shows what your config asks for; refresh checks the other direction — what happened at the provider outside managed (in the cloud console, for example). It asks each provider about every server managed ordered and reconciles:

  • Changes are saved. A new address, a resize, a different region, or a server powered off in the console is written back to managed's records, so later commands work with reality.
  • Gone servers are surfaced. A server that was deleted at the provider is reported as no longer existing. With --prune it's removed from the project (along with its service records). A server is only treated as gone when the provider definitively says so — a network hiccup or API error just skips that server, so a bad connection can never remove anything.
  • Unknown servers are listed. Servers running at the provider that this project doesn't track are listed, with a hint to bring one under management via managed server add.

Servers managed didn't order (imported/BYOB and adopted boxes) have nothing to check at a provider and are skipped with a note.

FlagDescription
--pruneRemove servers from the project when they no longer exist at the provider
--jsonOutput the result as JSON

managed server

Manage individual servers (VPS instances).

managed server create

Create a new server at your provider, outside of any service.

bash
managed server create --name worker-1 --region ams3 --size s-1vcpu-1gb

managed server add

Import an existing server (one managed didn't create) into the managed pool.

bash
managed server add --name legacy-web --ip 203.0.113.7

The server is probed over SSH, recorded, and from then on managed like any other box. Nothing is installed or changed until an apply targets it.

managed server scan

List servers at your cloud providers that aren't part of this project yet. Read-only — nothing is changed anywhere.

bash
managed server scan                      # ask every provider
managed server scan --provider do_prod   # ask one provider

managed server import

Import many existing servers at once — everything server scan found, a hand-picked set, or a pasted list of addresses. Each server is probed over SSH and added to the pool exactly like server add adds one; an unreachable box never stops the rest.

bash
managed server import --provider do_prod --all       # everything scan finds
managed server import --provider do_prod web-1 web-2 # pick by name, ID, or IP
managed server import [email protected] [email protected]
managed server import --file hosts.txt               # one user@ip per line

Afterwards, put the new servers on your private network:

bash
managed apply --only network

managed server discover

Probe an existing server's specs over SSH without saving it — a dry run of server add.

bash
managed server discover --ip 203.0.113.7

managed server list

List every server in the project with its addresses and status.

bash
managed server list

managed server bootstrap

Run (or re-run) the base setup on a server — users, SSH hardening, the managed agent.

bash
managed server bootstrap web-1

managed server destroy

Destroy one server at its provider.

bash
managed server destroy web-1        # asks for confirmation first
managed server destroy web-1 -f     # --force skips the prompt

See managed destroy for what is destroyed vs. kept — external/BYOB boxes are removed from the project but never deleted.

managed server trust

Accept a server's new SSH identity after you rebuilt or replaced it.

bash
managed server trust database-1

managed remembers each server's SSH identity from the first time it connects (see SSH keys — server identity) and refuses to connect when that identity changes, because a changed identity can mean someone is intercepting the connection.

If you changed the server on purpose — rebuilt it, restored it from an image, or reinstalled its operating system — run this command to forget the old identity. The next connection (for example managed apply) records the new one.

managed server sudo-password

Set, change, or clear the password a server's login user needs for sudo, so managed can run privileged steps on it. Boxes that log in as root or already have passwordless sudo need none.

bash
managed server sudo-password web-1                 # prompts, hidden input
MANAGED_SUDO_PASSWORD= managed server sudo-password web-1
managed server sudo-password web-1 --verify        # set, then test that sudo works
managed server sudo-password web-1 --clear         # box has passwordless sudo

The password is stored encrypted in the state file (never in managed.yaml). Prefer the MANAGED_SUDO_PASSWORD environment variable over --sudo-password to keep it out of your shell history. You can also set it when importing (managed server add/import --sudo-password, or the same env var), or declare it per-server in managed.yaml as an ${ENV} reference:

yaml
servers:
  web-1:
    ip: 203.0.113.7
    ssh_user: deploy
    sudo_password: ${WEB1_SUDO_PW}   # resolved from the environment; stored sealed

managed sudo-password

Set the project-wide default sudo password — a fallback used on any server that has no sudo password of its own. A per-server password (managed server sudo-password) and the MANAGED_SUDO_PASSWORD environment variable both take precedence over it.

bash
managed sudo-password                 # prompts, hidden input
MANAGED_SUDO_PASSWORD= managed sudo-password
managed sudo-password --clear         # remove the project default

Stored encrypted in the state file (never in managed.yaml). It's optional: leave it unset and servers with no password of their own just use passwordless sudo — which is what managed configures on every box it provisions (SSH-key login, NOPASSWD sudo, the account password locked), so a default is only useful for bring-your-own boxes whose login user needs a sudo password. The resolution order for any server is: its own password → MANAGED_SUDO_PASSWORD → this project default → passwordless.

managed destroy

Tear down infrastructure.

bash
managed destroy                          # destroy everything
managed service destroy production-db    # remove a single service
managed server destroy web-1             # destroy a single server

Destroys servers and cleans up state. managed destroy targets everything; managed service destroy <name> removes one service; managed server destroy <name> removes one server (after a confirmation that tells you exactly what will happen).

Removing a single service handles two kinds of server differently:

  • Servers the service owns (it provisioned them) are destroyed, managed service destroy lists them and warns the data is lost.
  • Servers it only runs on but doesn't own, a scope: fleet service on a shared box, or a co-located service, are uninstalled in place: the service is stopped and removed, but the box keeps running. These are listed separately as "uninstalled, box kept".

External servers (defined with ip in managed.yaml) are removed from state but never destroyed, managed never deletes servers it didn't create. Servers with protect: true are never destroyed by scale-down either (an explicit managed destroy still removes them).

Removing a service from managed.yaml and re-running apply has the same effect for the uninstall-in-place case: a removed fleet/co-located service is cleaned off every box automatically. Owned servers are left running and apply tells you to run managed service destroy to tear them down.

managed service migrate

Move a service's instances off a server that's no longer in its placement.

bash
managed service migrate production-db    # move stranded instances to eligible servers

When you edit a service's placement (or remove a server it was on), existing instances stay where they are by default — apply keeps placement sticky so re-runs don't churn, and shows the strand as a migrate action in the plan. To actually move them, run this command (or apply --allow-migrate):

  • A rebuild-safe node — any replica — is moved automatically: managed builds it on the new server (a replica reseeds from the primary), then retires the old node (drained in place for a BYOB box, destroyed for a provider-owned one).
  • A stateful primary is never moved automatically — that would lose data. The command prints the safe seed → promote → retire steps instead (mysql has the verify-parity / promote primitives to do it; other databases need a manual cutover).

managed service

Inspect installed services and run the commands their packages define.

managed service list

List installed services across all servers.

bash
managed service list

managed service run

Run a command a service package defines (backups, restores, deploys, …).

bash
managed service run production-db backup
managed service db backup                      # the two-word shorthand works too
managed service run mysql create-db --server my-db-server --arg db-name=mydb

The name is your configured service from managed.yaml or the package name. The target server is auto-detected when the service runs on exactly one box; use --server (or --service <instance-id>) to pick one otherwise. --arg k=v passes per-invocation arguments to the script.

managed service remove

Remove a service but keep the servers you brought yourself (BYOB/adopted boxes are uninstalled in place, never destroyed).

bash
managed service remove cache

managed service destroy (see managed destroy) also tears down the servers the service provisioned; managed service migrate is documented below.

managed repl

Open an interactive client (mysql CLI, psql, redis-cli, …) connected to a service.

bash
managed repl production-db              # connect to the service's primary
managed repl mysql-a3f2                 # or a specific instance by ID
managed repl production-db --role replica

Looks up the service's connection details and credentials, picks the address you can actually reach (public, or the private/overlay address for a firewalled database), and starts the client. The password is handed to the client through its environment — it never appears on a command line, so it can't be read from your shell history or by other users of your machine in the process list.

managed providers

Work with cloud providers and their catalogs.

bash
managed providers verify                 # test the credentials in managed.yaml
managed providers regions <provider>     # list available regions
managed providers sizes <provider>       # list sizes (optionally per region)
managed providers catalog <provider>     # full provisioning catalog as JSON
managed providers schema                 # provider auth schemas as JSON (used by the dashboard)

managed catalog

List every service package available from your configured repositories (or the official repository when none are configured). With --json it emits each package's full config-form schema.

bash
managed catalog

managed reserved-ip

Manage retained reputation/egress IPs — provider reserved/floating IPs that outlive a server, so a box can be replaced without losing its sending or crawling reputation. Declare one with reserved_ip: in managed.yaml; managed apply attaches it automatically.

bash
managed reserved-ip list             # retained IPs managed knows about
managed reserved-ip attach web-1     # attach the server's configured reputation IP
managed reserved-ip detach web-1     # detach it (the IP is kept, never released)

managed export

Reconstruct a managed.yaml from the project's current state — the cloud project, if connected — so a setup edited in the web UI can be checked back into git.

bash
managed export

managed lock

View the cloud project lock that prevents concurrent applies on a connected project. managed lock release force-releases a lock left behind by a crashed run — only do this when you're sure no apply is still running.

bash
managed lock
managed lock release

managed key

Manage the key that encrypts this project's secrets. Service passwords, storage credentials, and other secrets are stored encrypted inside .managed/state.json (and in the dashboard's copy of it); the key lives outside the project at ~/.config/managed/keys/, so a committed or leaked state file contains only ciphertext. The key is created automatically the first time a secret is saved.

Projects connected to managed.works share the key automatically: the CLI uploads it to the dashboard (protected by the dashboard's own encryption) when it syncs, and managed ui connect downloads it when you adopt the project on a new machine — so teammates and CI usually never handle it by hand.

For a local-only project, print the key with managed key export --reveal, store it in a password manager, and install it on the other machine with managed key import <key>. Without the key, a machine refuses to use the project's encrypted secrets and says so.

bash
managed key export --reveal   # print the key once (store it in a password manager)
managed key import <key>      # install the key on this machine

managed login / managed logout

Log this machine in to (or out of) your managed.works account. Login opens a browser; once logged in you can create hosted package repositories, publish to them, and connect projects to the web dashboard.

bash
managed login
managed logout

managed ui

Connect or disconnect this project from the managed.works web dashboard.

bash
managed ui connect        # link the project (adopts the cloud copy by default)
managed ui disconnect     # unlink it

managed agent

Manage the on-box agent that answers dashboard queries. managed agent start runs the agent server — it's what the systemd unit on a managed box executes; you rarely run it by hand.

managed agent update

Update the managed agent on your servers without redeploying services. Downloads the latest agent binary, restarts the agent process, and leaves everything else untouched.

bash
managed agent update                    # update all servers
managed agent update --server my-server # update a specific server

Useful when you've made changes to the agent and want to roll them out quickly. All servers are updated in parallel by default.

FlagDescription
--serverTarget a single server by name (default: all servers)

managed backup

Trigger an immediate backup for all services that have backup schedules configured.

bash
managed backup

managed ssh

SSH keys and connections to managed servers.

managed ssh connect

Open an interactive SSH session to a managed server by name.

bash
managed ssh connect --name web-1

managed ssh generate-key

Generate a new Ed25519 SSH key pair on this machine.

bash
managed ssh generate-key
managed ssh generate-key --name my-do-key --dir ~/.ssh

The key is saved locally — in --dir, or the ssh_key_directory default from managed.yaml, or .managed/ssh_keys in the project. Nothing is uploaded anywhere by this command; managed apply installs the public key on servers it creates when the key is configured as the project's SSH key. An existing key file is never overwritten.

managed repo

Manage service repositories.

managed repo add

Add a remote service repository.

bash
managed repo add managed.works                          # official repo
managed repo add github.com/my-org/my-services          # git repo
managed repo add github.com/my-org/my-services --ref v2 # pin to tag

Clones the repo, validates it contains service definitions, and adds it to your managed.yaml.

managed repo remove

Remove a previously added repository.

bash
managed repo remove my-services

managed repo list

Show configured repos, their URLs, pinned refs, and available services.

bash
managed repo list

managed repo update

Fetch the latest version for all repos (or a specific one).

bash
managed repo update            # update all
managed repo update my-services # update one

managed repo pull

Pull the registry repositories configured in managed.yaml (used by fresh workspaces — e.g. the dashboard — before resolving packages).

bash
managed repo pull

managed repo create

Create a hosted package repository on managed.works (requires managed login).

bash
managed repo create my-packages

Add a hosted managed.works repository to this project.

bash
managed repo link my-packages

managed repo keygen

Generate an Ed25519 signing keypair for publishing packages. Published packages are signed with it; boxes verify the signature before installing.

bash
managed repo keygen

managed repo push

Publish a package to your hosted repository. Versions are immutable — bump the version: in service.yaml before publishing a change.

bash
managed repo push services/mysql

managed repo reindex

Rebuild the hosted repository's index from the bucket's contents (repair tool — normally push maintains the index).

bash
managed repo reindex my-packages

managed project

Register named project directories so managed commands can be run from anywhere. See the Projects guide for the full picture.

managed project save

Register the current directory as a named project.

bash
managed project save feeder              # register cwd as "feeder"
managed project save feeder --default    # also make it the default
managed -C ~/code/feeder project save feeder   # register without cd-ing

managed project switch

Print export MANAGED_PROJECT=<name> so the surrounding shell can apply it with eval. Every following managed command in that shell uses the named project.

bash
eval $(managed project switch feeder)

managed project default

Set the default project. Used when nothing else picks a project (no --project, no MANAGED_PROJECT, no managed.yaml in the current directory).

bash
managed project default feeder

managed project list

List every registered project, mark the default, and show which project is active in the current shell.

bash
managed project list

managed project remove

Forget about a project. The directory and its files are not touched.

bash
managed project remove feeder

managed version

Print the managed version.

bash
managed version

Global flags

These flags work on every managed command:

  • -v, --verbose, print debug logs.
  • -C <path>, --directory <path>, change to <path> before running.
  • -P <name>, --project <name>, use a registered project for this command. Equivalent to -C with the project's saved path.