Search the docs

Find a page or a section of the docs

Deploy now

CLI

Every moin command

Markdown

Every command of moin. Where things work shows what the CLI, the web, the terminal UI, the API and MCP each can do.

Command Does
moin opens the terminal UI
moin login [--url <url>] [--web] [--token <token>] logs this computer in through the browser: prints a code, opens /device and waits until you approve it there; without a browser, open the printed address on any device. --web does it without a terminal too. For CI and scripts, --token or a token on stdin (echo "$TOKEN" | moin login) skips the browser. --url defaults to the saved control plane, else https://moin.run. It must be https; plain http works only for localhost and your tailnet (100.x addresses, *.ts.net). See Log in the CLI
moin logout forgets it, and what the terminal UI kept for its next start
moin alerts [on|off] shows or sets whether you get a mail when a site goes down or comes back, a server goes offline or comes back, or a deploy fails, in the workspaces you own or run as admin; on by default. See Alerts
moin alerts add slack|discord|telegram|webhook <name> [--chat <id>] [--events <events>] sends alerts to Slack, Discord, Telegram or a webhook too; the URL, or the bot token for Telegram, comes on stdin, --chat is the Telegram chat, --events a comma list like uptime.down,deploy.failed (default all). A webhook prints its signing secret once. moin alerts lists the destinations. Owners and admins. See Alerts
moin alerts test <name> sends a test alert to a destination
moin alerts rm <name> [--yes] removes a destination
moin activity [--app <app>] [--server <server>] [--user <email>] [--limit 30] [--cursor <cursor>] who deployed, restored or changed what, newest first, to the second in your time zone; the hint at the end pages back
moin team members and open invitations of your workspace
moin team invite <email> [--admin] invites someone by email
moin team resend <email> sends an invitation again; only the new link works
moin team revoke <email> [--yes] takes an invitation back
moin team rename <name> renames the workspace; its links stay the same
moin status is everything OK: what needs you, catalog updates and what runs; exits 1 when something needs you (Overview)
moin servers lists servers
moin servers add [name] adds a server, prints its install command; without a name it gets one like brave-otter
moin servers install-command <name> prints a new one-time install command; the old one stops working
moin servers rename <name> <new name> renames a server; its apps stay on it
moin servers move <name> up|down|top|bottom|<position> moves a server in the order every list shows; a position counts from 1
moin servers rm <name> [--yes] asks you to type its name, removes the server and prints the command that removes its agent there
moin servers update [name] updates the agent to the control plane’s version now; without a name every online server whose agent differs
moin servers auto-update [on|off] shows or sets whether agents update to the control plane’s version by themselves; on by default
moin servers rules <name> the server’s own rules for moin, from /etc/moin/rules on it
moin apps lists apps
moin apps <app> one app: its state, server, domains, password, repo and deploys; moin app <app> works too
moin apps add --server <server> <name> adds an app
moin apps rm <app> [--yes] asks you to type its name, removes the app, keeps its data
moin apps move <app> up|down|top|bottom|<position> moves an app in the order every list and the sidebar show; a position counts from 1
moin deploy [--dir .] [--server <server>] [--detach] [app] deploys a directory
moin deploy --image <image> [app] deploys an image built in CI, with the compose file of an earlier deploy of the repo
moin redeploy [app] runs the newest source or image again
moin restart [app] starts the live release again with the current env and domains
moin stop [app] [--yes] takes the app offline; its data stays
moin start [app] runs a stopped app again
moin deploys [app] the newest deploys: number, status, started, took, note
moin deploys [app] v<number> one deploy with its whole log
moin hook [app] [--yes] asks, then prints a new deploy hook URL for CI; the old one stops working
moin hook off [app] [--yes] turns the deploy hook off; CI can no longer deploy
moin registry lists registry logins
moin registry set <host> <username> saves a login; the password or token comes from stdin
moin registry rm <host> [--yes] removes a login
moin rollback [app] [v<number>] [--yes] starts an earlier release again; without a number the one before the live one
moin logs [--tail 200] [app] prints container logs
moin exec [app] [--service web] [--timeout 10m] -- <command> runs a command in a container, no shell; exits with its code, 124 after the timeout (owner or admin)
moin domains [app] lists domains and their DNS
moin domains add [--service <service>] <domain> ... adds domains, to another service of compose.yml with --service
moin domains rm [--service <service>] <domain> ... [--yes] removes domains
moin domains canonical <domain|none> the domain the others redirect to
moin instant [--show] [app] the instant address, with the password moin made for it with --show
moin instant sslip.io|nip.io|off [app] picks the provider of the instant address, or turns it off
moin instant open|protect [app] takes the password away from the instant address, or puts it back
moin protect [--user moin] [app] asks for a password; the password comes from stdin
moin protect off [app] [--yes] makes the app public again
moin uptime [app] uptime of every domain over 24 hours, 7 and 30 days, the newest check and the last outages
moin uptime path <domain> <path|default> where the check of a domain goes; default is the health path or /
moin uptime pause <domain> · moin uptime resume <domain> pauses the check of a domain, checks it every minute again
moin metrics [app|server] [--range 1h|24h|7d|30d] CPU, memory and network of an app now, with a sparkline of the range and the deploys that went live; a name that is no app is a server
moin metrics --server <server> CPU, memory, disk, network and load of a server, and its apps by memory
moin db [--db <path>] [app] tables, row counts and migration of the database
moin db rows <table> [--limit 20] [--offset 0] a page of a table, newest first
moin db schema [table] the CREATE statements
moin sql [--app <app>] [--db <path>] [--limit 100] "<sql>" runs SQL read-only; the SQL may come on stdin
moin sql --write "<sql>" [--yes] runs SQL that changes data, in one transaction; DROP and DELETE ask first
moin backups [app] snapshots, point in time window and restore drills
moin backup [app] takes a snapshot now
moin restore <snapshot> [--yes] · moin restore --at "<time>" [--yes] restores a snapshot, or a point in time in UTC: "2026-10-31 14:01", or 14:01 for today; --db <path> picks one of several databases, --bucket <bucket> restores a snapshot from an older bucket the app left
moin backups auto on|off [app] snapshot before every change
moin buckets your buckets, the default first, with the apps that use each and the apps that keep older backups there
moin buckets add <name> --endpoint … --bucket … --access-key … [--region auto] [--path <folder>] [--default] adds a bucket; the secret key comes on stdin; the first one is the default
moin buckets edit <name> [--name <new>] [--endpoint …] [--bucket …] [--path …] [--region …] [--access-key … | --secret] changes a bucket; a new access key, or --secret, reads the secret key on stdin, otherwise the stored one stays
moin buckets default <name> makes a bucket the default for apps without one of their own
moin buckets rm <name> [--yes] removes a bucket from moin, refused while apps use it or it keeps older backups; what is in it stays
moin buckets use <bucket> [app] · moin buckets use --default [app] moves an app’s uploads and new backups to a bucket, or back to the default; older backups stay where they are
moin backups bucket · bucket set · bucket rm [--yes] the default bucket, as before there were several
moin deploy --ref <ref> [app] deploys a branch, tag or commit of the linked GitHub repo
moin catalog [app] lists the catalog with each app’s newest release, or one app with every release
moin catalog install <app> --server <server> [--version <tag>] [--name <name>] [--domain <domain>] installs a catalog app and follows its first deploy
moin update <app> [--version <tag>] sets a catalog app to a release, the newest by default, and deploys it after a snapshot
moin github · moin github repos where the GitHub App is installed, with the install link; the repos it reads and where to give it more
moin github branches <owner/name> the branches of a repo, the default first
moin link owner/name [--branch main] [--auto=false] [app] follows a repo; every push deploys unless --auto=false
moin unlink [app] [--yes] stops following it
moin servers secure <server> [--tunnel on|off] [--ssh tailscale|open] [--firewall on|off] [--yes] how the server is reached; prints what the agent set up
moin integrations · integrations cloudflare · integrations tailscale · integrations rm <name> [--yes] the Cloudflare API token and the Tailscale auth key; keys come on stdin
moin env [--show] [app] lists env vars, values hidden without --show
moin env set [app] KEY=value … sets env vars
moin env rm KEY … [--yes] removes env vars
moin env pull [--app <app>] [--yes] [file] writes them to .env; asks before it replaces a file
moin env push [--app <app>] [--yes] [file] replaces all env vars with a .env (default .env); shows the keys that change and asks first
moin run [--app <app>] -- <command> runs a command with them
moin agent register --url <url> --token <install token> connects a server, done by the install script
moin agent runs the agent
moin agent uninstall [--apps] [--yes] removes the agent from this server; the apps keep running unless --apps
moin version prints the version of moin and of the control plane, and says when a newer release is out
moin update [--yes] updates a self-hosted control plane to the newest release, for its admins, and waits until it is back

Commands that work on an app take it as an argument or as --app, then from x-moin.app in compose.yml, then from the directory name. Flags may come before or after the arguments: moin rollback v3 --app <app-name> is moin rollback --app <app-name> v3. When there is no such app, the error names it and where the name came from. Every command shows its usage and flags with --help.

Asking first

moin asks before it removes or replaces something. How it asks depends on how big the change is:

Commands Asks
Big, cannot be undone servers rm, apps rm, buckets rm, backups bucket rm, restore type the name: Type <app-name> to confirm:
Small or reversible update, stop, rollback, env rm, domains rm, protect off, registry rm, integrations rm, unlink, hook, hook off, team revoke, servers secure, agent uninstall, env pull over an existing file, env push, sql --write with DROP or DELETE [y/N]
Harmless deploy, redeploy, restart, start, backup, adding and setting things nothing

--yes skips the question, the typed name too. Without a terminal, as in CI, they refuse unless --yes is there, so a script never removes something by accident. The web, the TUI and agents ask the same way; the API never asks.

Agent updates

Agents update themselves to the control plane’s version, unless moin servers auto-update off. moin servers shows each agent’s version and marks one that differs from the control plane’s. moin servers update has the agent download the moin binary the control plane serves, from the control plane URL it already uses, check its SHA-256, swap it in and restart through systemd. It waits until the new agent is back. The agent refuses while a deploy runs, and on a server with rules in /etc/moin/rules, since a new binary could allow what they stop. There, and for agents older than the update, update it on the server as root:

curl -fsSL https://moin.run/download/moin-linux-amd64 -o /usr/local/bin/moin.new && chmod 755 /usr/local/bin/moin.new && mv /usr/local/bin/moin.new /usr/local/bin/moin && systemctl restart moin-agent

Use moin-linux-arm64 on an ARM server. The error of a refused update prints the command for that server.

/download/SHA256SUMS lists the checksums of every binary.

Output

On a terminal the CLI shows colors, status symbols (● online, ◐ deploying v4, ✗ failed v4 · v3 live) and a spinner while a deploy runs. Piped or in CI it prints plain text: tables keep their columns with the bare status words, so scripts can read them. Errors start with ✗, name the command to run next and exit with 1.

Environment

Variable Does
MOIN_URL, MOIN_TOKEN override the saved login, for CI
MOIN_STATE_DIR where the agent keeps its state, default /var/lib/moin
NO_COLOR turns colors off on a terminal too