What you need
- An account on moin. Sign up on the hosted control plane, or host it yourself.
- A Linux server that you can log in to as root, on amd64 or arm64. Rent one at any provider, such as Hetzner.
- A bucket, an online storage box at Cloudflare R2, AWS S3, Backblaze B2, Hetzner or Exoscale. Your bucket explains why moin needs one.
- Your app in a folder: with a
compose.yml, aDockerfileor ago.mod, or none of them for Node, Bun, Python, Ruby, PHP and more. Deploy says what moin does with each. - A domain, if people should reach the app in a browser. You can add it at the end.
Not sure what a word means? How moin works in 2 minutes explains each one.
1. Sign up
Open the control plane in your browser and press Continue with GitHub. No GitHub account, or a self-hosted control plane without GitHub sign-in? Enter your email instead, and moin sends you a sign-in link. GitHub finds your account by any verified email on your GitHub account, so an account made with your work address works too.
The first time, moin asks for two things:
- Your name, the one your team sees.
- Workspace name, your team’s space for servers, apps and the bucket.
With GitHub both come filled in from your GitHub profile, such as “Ada Lovelace” and “Ada Lovelace’s Workspace”, so you press Continue and are done. With email you type them. You can change both later in the settings. When a teammate invited you, moin asks only for your name, and you join their workspace.
On a self-hosted control plane, only the admins and invited teammates sign up: see Who signs in.
2. Install moin on your computer
On a Mac or Linux:
curl -fsSL https://moin.run/cli.sh | sh
It puts moin in /usr/local/bin, or in ~/.local/bin when that is not writable. If it says to add ~/.local/bin to your PATH, add this line to ~/.zshrc or ~/.profile and open a new terminal:
export PATH="$HOME/.local/bin:$PATH"
The commands on these pages name the control plane you read them on.
3. Log in
moin login --url https://moin.run
moin prints a code like WDJB-MJHT and opens your browser. Sign in there if you are not yet, check that the page shows the same code, and press Approve. The terminal says Logged in as with your name.
No browser on this machine, for example over SSH? Open the address moin prints on any other device, your phone too, and approve it there.
The login belongs to this computer and your workspace. It shows as CLI on and the computer’s name under Settings, Tokens, and ends after 90 days without use. Account and tokens has more, and how CI logs in with a token.
4. Add a server
Pick a name for the server, such as web-1. moin gives you one command to run on it.
Servers, Add server
Log in to your server as root, for example with ssh root@<server-ip>. Then run the command moin printed, within the next hour. It looks like this:
curl -fsSL https://moin.run/install.sh | sh -s -- ins_…
Not logged in as root? Put sudo before sh.
It installs Docker when it is missing, which takes a few minutes. Then it installs the agent as the moin-agent service and connects it. moin servers now shows your server as online. The web shows the same under Servers, live. Servers has more.
5. Add your bucket
Your code and your backups go into your own bucket, never to us.
- Create a bucket at your storage provider.
- Create an access key for it that may read, write and delete. You get an access key and a secret key.
- Find the endpoint: the web address of the storage. For Cloudflare R2 it is
https://<account-id>.r2.cloudflarestorage.com. It must start withhttps://. For AWS S3, leave it empty and set the region of the bucket instead.
Then tell moin:
Buckets in the sidebar, then Add bucket
The CLI asks for the secret key. moin writes a test file and deletes it, so a wrong key fails right away. Your bucket has the details.
6. Deploy
Go to the folder of your app and run:
moin deploy
The first deploy creates the app, named after the folder, on your only server. If you have more than one server, add --server <server-name>. moin packs the files git knows, the agent on your server builds and starts them, checks that they stay up and prints the log as it goes:
Created app <app-name> on <server-name>
Uploading 42 files to your bucket (18.3 KB)
Deploy v1 of <app-name> started
detected go, service web on port 8080
building
…
v1 is live
✓ moin. <app-name> is live at https://<app-name>.<server-ip-with-dashes>.sslip.io
If the deploy fails, the log ends with the reason. Read it again any time with moin deploys v1. Deploy explains what moin detects and how to change it.
7. Open it
Your app is already online at its instant address, with HTTPS and a password in front: moin instant --show prints both. On a server behind a Cloudflare Tunnel there is none, since it needs ports 80 and 443.
For your visitors, give it a domain:
the app page, Domains, Add domain
moin shows the address of your server and the DNS record to set. Set it where you bought the domain, in its DNS settings: an A record that points the domain at the server’s address. DNS can take a few minutes. moin domains shows when the domain points at your server, and then the app has HTTPS. Domains and access has the details.
What next
- No code of your own? Install n8n, Umami, Open WebUI and 44 more in one click: Catalog.
- Settings and secrets for your app: Env vars.
- Your database, SQLite or Postgres, every change backed up, every restore tested: Backups.
- Close every port of the server: Security.
- Deploy every push to GitHub: GitHub.
- Run a migration or make the first admin: Run a command.
- Hear when a site goes down, a backup stops or a disk fills up: Alerts.
- Bring your team: Team and activity.