Deploy your projects to your own servers over SSH: releases, Docker Compose, blue/green, rollbacks, live logs, and an MCP server for AI agents. Laravel 13 + Livewire 4. Made with Claude ❤️
Overview
🇬🇧 English · 🇺🇦 Українська Quick start · Install · User guide · MCP for AI agents · Architecture · Security is a local, Dockerized admin app that deploys your projects to . You run it on your Mac at https://deployme.localhost. - Servers come straight from your ~/.ssh/config. - Projects come from GitHub or from a local folder. - Every run is recorded with a live, redacted log. - It deploys with releases and an atomic switch, or in place. - It builds or pulls images, migrates, starts the new containers beside the old ones, and rolls back by itself when the health check says no. You don't need a PaaS, a cloud control plane or an agent on the server: just ssh, rsync and docker compose on the other end. deploy.me runs real production sites. Every feature below is built, tested, and in use. Added from ~/.ssh/config. Host keys are trusted on first use and pinned after that.
README
🇬🇧 English · 🇺🇦 Українська
Quick start · Install · User guide · MCP for AI agents · Architecture · Security
deploy.me is a local, Dockerized admin app that deploys your projects to your own
servers over SSH. You run it on your Mac at https://deployme.localhost.
- Servers come straight from your
~/.ssh/config. - Projects come from GitHub or from a local folder.
- Every run is recorded with a live, redacted log.
- It deploys with releases and an atomic switch, or in place.
- It builds or pulls images, migrates, starts the new containers beside the old ones, and rolls back by itself when the health check says no.
You don’t need a PaaS, a cloud control plane or an agent on the server: just ssh,
rsync and docker compose on the other end.
[!NOTE] deploy.me runs real production sites. Every feature below is built, tested, and in use.
✨ Features
Also included: 🔁 one-way folder mappings (Mac ↔ server, rsync, previews, capped deletes), 🔑 passkey or password sign-in (with optional authenticator codes), and 💾 nightly backups of deploy.me itself.
🚀 Quick start
All you need is Docker. deploy.me runs the same way on all three systems and brings its own PostgreSQL, Valkey and HTTPS edge, unless you already run a shared stack it can join.
🍎 macOS · 🐧 Linux
You need Docker Desktop or OrbStack on macOS, or Docker Engine with the Compose plugin on Linux. Then run this one line:
curl -fsSL https://raw.githubusercontent.com/KielD-01/deploy.me/main/install.sh | bash
It clones deploy.me into ~/deploy.me and runs bin/install. Read
install.sh first if you like. To do the same by hand:
git clone https://github.com/KielD-01/deploy.me.git
cd deploy.me
bin/install
🪟 Windows
You need WSL2 and Docker Desktop with WSL integration turned on. Then, in PowerShell:
irm https://raw.githubusercontent.com/KielD-01/deploy.me/main/bin/install.ps1 | iex
bin/install does everything in one go:
- checks the tools it needs;
- describes your machine in
.env(bin/configure); - pulls the ready-made PHP image from Docker Hub (amd64 and arm64), so nothing compiles, and starts the stack;
- trusts the local CA in your browsers (macOS keychain, Linux NSS, Windows’ store);
- prints a one-time link that sets up your passkey or a password.
The per-OS notes, the two stack modes and troubleshooting are in docs/install.md.
Open https://deployme.localhost, then:
- Servers → Add server. Pick a host from
~/.ssh/config, trust its key, choose a deploy path. - Projects → Import project. Pick a GitHub repository or a local folder.
- Targets → Assign to server, then Prepare.
- Deploy 🎉
[!WARNING] Back up
APP_KEYfromsrc/api/.env. Stored tokens and env files are encrypted with it.
Want a safe playground first? make sandbox starts a local SSH + Docker server with the
alias sandbox.
🤖 Talk to it
# Read-only: look, explain, plan, probe
claude mcp add deployme -- docker exec -i deployme-web php artisan deployme:mcp
# …or allow deploys, rollbacks and cancels too
claude mcp add deployme-ops -- docker exec -i deployme-web php artisan deployme:mcp --allow-deploys
💬 “What’s deployed where, and is anything unhealthy?”
💬 “Why did run #42 fail? Show me the step that broke.”
💬 “Is there a green build on main that isn’t live yet? Deploy it to staging and watch it.”
Setup for Claude Desktop, Cursor and VS Code, the full tool list and the safety model are in docs/mcp.md.
🗺️ How a deploy flows
flowchart TB
A([🧑 You · 🤖 agent · 🟢 CI]) --> B[🎯 Resolve strategyGHCR · archive · rsync · git]
B --> C[🔒 Preflightlock · disk · stack]
C --> D[📤 Upload releaseor pull pinned images]
D --> E[🧪 compose config]
E --> F[💾 pg_dump]
F --> G[🗃️ Migrate]
G --> H[🔵🟢 Roll / recreateswitch current]
H --> I{🩺 Health gate}
I -- ✅ healthy --> J[🧹 Prune releases& images]
I -- ❌ failing --> K[⏪ Auto-rollback]
classDef start fill:#a78bf0,stroke:#6d4ac7,color:#0e1116
classDef step fill:#2a5bd7,stroke:#1f45a8,color:#fff
classDef gate fill:#6d4ac7,stroke:#4c3291,color:#fff
classDef good fill:#0f766e,stroke:#0b5750,color:#fff
classDef bad fill:#d97757,stroke:#b4573a,color:#fff
class A start
class B,C,D,E,F,G,H step
class I gate
class J good
class K bad
🏗️ Architecture
flowchart LR
subgraph mac["💻 Your Mac"]
browser["🌐 Browserdeployme.localhost"]
agent["🤖 MCP client"]
subgraph app["deploy.me containers"]
web["deployme-webFrankenPHP · Livewire"]
worker["deployme-workerqueues · scheduler"]
reverb["deployme-reverbwebsockets"]
end
subgraph infra["shared infra · proxy-network"]
caddy["Caddy edge"]
pg[("PostgreSQL")]
valkey[("Valkey")]
end
end
servers[("🖥️ Your servers")]
github["🐙 GitHub · GHCR"]
browser --> caddy --> web
caddy --> reverb
agent -- "stdio · docker exec" --> web
web --> pg
web --> valkey
worker --> pg
worker --> valkey
worker -- "ssh · rsync" --> servers
worker -- "API" --> github
classDef c fill:#2a5bd7,stroke:#1f45a8,color:#fff
classDef i fill:#0f766e,stroke:#0b5750,color:#fff
classDef x fill:#6d4ac7,stroke:#4c3291,color:#fff
class web,worker,reverb c
class caddy,pg,valkey i
class servers,github,agent,browser x
| Service | Role |
|---|---|
deployme-web |
FrankenPHP serving the app. The shared edge routes deployme.localhost to it, and websocket requests on /app to Reverb |
deployme-worker |
Supervisor: the queue workers (redis, redis-deploys, redis-probes) and the scheduler. The only container that mounts project folders read-write |
deployme-reverb |
Reverb websockets: live logs, server status, CI updates |
deployme-vite |
Vite dev server with HMR (dev profile only) |
deployme-sshd-target |
A local sshd + Docker sandbox server (sandbox profile only) |
🧰 Everyday commands
| Command | What it does |
|---|---|
make up · make down · make restart |
Start, stop or restart the stack |
make ps · make logs s=deployme-worker |
Status; follow logs (s= for one service) |
make enroll |
A one-time link to set up a passkey or password (first run, or a lost way in) |
make doctor |
Check infra, containers, TLS, websockets, every server alias and the GitHub tokens |
make dev · make assets |
Vite with HMR · build the frontend |
make test · make sandbox · make test-ssh |
The Pest suite · start the SSH sandbox · run the real-SSH tests against it |
make backup · make backup-install · make backup-status |
Back up deploy.me’s database and APP_KEY |
make artisan c="…" · make sh |
Artisan, or a shell, in the web container |
make help |
Every target |
🛡️ Security model
- 🔑 Passkeys first, a password if you need one. The password needs at least 12
characters, is rate-limited, and can require an authenticator code. There’s one account:
the first visit (or
make enroll) creates it, then the page is gone, and the last way in can’t be removed. Trusting a host key, touching env files, deleting during a sync, adding or removing tokens, and changing how you sign in all ask you to confirm it’s you again. - 🤫 Secrets never go in argv or logs. Remote scripts receive them over stdin, git gets
tokens only through
GIT_ASKPASS, and the step logger redacts before anything is stored or broadcast. - 🔐 Encrypted at rest. GitHub tokens, env files and the Telegram token are encrypted
with
APP_KEY. - 📌 Pinned host keys. Trusted on first use and stored in the database. A changed key stops the connection.
- 👀 The probe reads names and counters, never command lines. Its network sample runs
read-only
ssundersudo -n, and is skipped where sudo would ask for a password. - 🚧 Guard rails. Local imports stay inside the project roots, nothing is pushed into a
reserved server path (
/opt/infra), and the MCP server is read-only unless you say otherwise.
🧪 Tests
make test # 480+ Pest tests, in the container, against deployme_test
make sandbox # then:
make test-ssh # the deploy scripts for real, against the local SSH sandbox
tests/Feature/Deploy/PipelineMatrixTest.php guards which steps run for each deploy mode ×
delivery strategy. tests/Feature/QueueConfigTest.php keeps every queue’s retry_after
above its worker timeout.
❤️ Made together with Claude
deploy.me was designed and built by Roman Kozin together with Claude, Anthropic’s AI, working as a pair in Claude Code. They worked through the plan, the design canvas, the deploy engine and its bash scripts, the tests, the MCP server and this README, one commit at a time. Roman made the calls and Claude wrote most of the code with him. 🤝
📄 License
MIT © 2026 Roman Kozin. Use it, fork it, ship with it.
Built in Ukraine 🇺🇦 with ☕, 🐳 and a lot of ssh.
Install
This server does not publish a one-line install command.
Open the repository installation guide