How to Self-Host KiroCrew on VPS 24/7
Run KiroCrew for VPS with Docker and systemd so memory, schedules, and approval queue survive reboot. Learn SSH access, backups, rollback, and port 5476 safety.
Why you suppose self-host KiroCrew for VPS instead of laptop
Self-hosting KiroCrew only make sense for machine wey no dey sleep, so VPS na the correct place for am; laptop no be. KiroCrew dey keep session history, semantic memory, scheduled jobs, and approval queue for disk, then e dey load all of dem again when process restart. All this no go help if process no dey run by 03:00 when scheduled job suppose run, and closed laptop no dey run am.
KiroCrew na open source agent workspace from Kiro team, under Apache 2.0 license. E first public releases come out for early August 2026. One process wey dem call gateway dey control the state and serve web dashboard for port 5476. You fit reach gateway from dashboard, from the kirocrew CLI, or from chat channel like Slack. Gateway na the only thing wey you dey self-host, so this guide dey focus on keeping am running, keeping am away from public internet, and restoring am after bad upgrade.
Make you know two things before you start. KiroCrew dey drive kiro-cli, and e need one-time sign-in with Kiro account. Agent inference dey bill against Kiro plan, so as of August 2026, this setup no dey work offline. The project still dey only weeks old. Assume say you go need roll back at some point, and install am in a way wey go allow you do that. If you never run agent for server before, run coding agent for VPS explain the basic rules wey this guide dey build on.
Wetin KiroCrew need, and where e dey keep im state
Native install need Python 3.10 or newer (the project recommend 3.12), Node.js 18 or newer if you build the dashboard from source, and kiro-cli, wey first launch go install and sign in for you. Container install no need any of these things for the host. E need Docker. Na the main reason to prefer am.
State dey for ~/.kiro/crew, and KIROCREW_HOME environment variable fit move am go another place. Wetin dey inside:
config.json: gateway settings and chat channel credentials..env: secrets.workspace/memory/: preferences, project notes and chat history.memory.dbandmemory_index.db: semantic and full-text indexes.models/: embedding model, wey download for first run.gateway.logandsecurity_events.jsonl: runtime log and security event log.
That directory na the install. Copy am go new VPS and you don move your agent. Na why the backup section below matter pass the install section.
Plan for disk instead of RAM. Gateway na Python process. Wetin really dey load the box na anything wey the agent run, like build or test suite. State directory dey grow as chat history increase, and embedding model go arrive for first start. So measure am for your own box with du -sh ~/.kiro/crew after some weeks, instead of trusting any figure wey dem publish for the first month of the project.
Wetin be the right one among the three installation paths to use
The project publish three. The one-line installer dey fetch one wheel and put kirocrew for your PATH:
curl -fsSL https://download.crew.kiro.dev/cli.sh | shE dey take one channel flag and one version flag:
curl -fsSL https://download.crew.kiro.dev/cli.sh | sh -s -- --channel insider
curl -fsSL https://download.crew.kiro.dev/cli.sh | sh -s -- --version 0.1.3Dem publish the container image for ghcr.io/kirodotdev/kirocrew, for linux/amd64 and linux/arm64 under every tag. The source build na git clone plus make build, and na for people wey dey change the code, no be for people wey dey run am.
Use the container. Native installation go put Python packages, Node and kiro-cli for the same host wey dey run your other services. So, if upgrade get problem, you go need dey undo the changes by hand. The container keep the runtime inside one image and the state inside one volume. This make rollback na just to change tag and restart.
Pin the image to a release tag, no be stable
The project example itself dey use stable tag:
docker run -d --name kirocrew \
-p 127.0.0.1:5476:5476 \
-v kirocrew-home:/home/kirocrew \
ghcr.io/kirodotdev/kirocrew:stablestable na moving tag. E dey point to whichever stable release be the newest at that time. So, the next pull fit change the version wey you dey run without you choosing am. The tag no record which version e be. Version tags no dey change, so pin one. The newest release as of 6 August 2026 na 0.1.3, wey dem publish on 5 August 2026. nightly tag dey too. For project wey still young like this one, that one mean say the code change this morning.
Write /opt/kirocrew/compose.yaml:
services:
kirocrew:
image: ghcr.io/kirodotdev/kirocrew:0.1.3
container_name: kirocrew
restart: unless-stopped
ports:
- "127.0.0.1:5476:5476"
volumes:
- kirocrew-home:/home/kirocrew
volumes:
kirocrew-home:Start am, then check the health endpoint wey the image also use for e own HEALTHCHECK:
cd /opt/kirocrew
docker compose up -d
docker compose ps
curl -s http://127.0.0.1:5476/api/healthdocker compose ps suppose report say the container healthy within about one minute. /api/health dey answer without token, just like /api/live and /api/ready. Na this make dem useful as probes. If the state remain starting, read docker logs kirocrew before you change anything. The first run downloads the embedding model, so slow link fit make the first start take long.
Systemd dey keep am running
restart: unless-stopped go bring the container back after crash and after reboot, as long as Docker itself start when system boot. Unit file dey make this dependency clear and give you one command wey go stop the whole stack before backup. Start Docker Compose stack when system boot explain the general pattern. Na this be the KiroCrew setup, for /etc/systemd/system/kirocrew.service:
[Unit]
Description=KiroCrew gateway
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/kirocrew
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now kirocrew
systemctl status kirocrewsystemctl status kirocrew suppose read active (exited), wey be the healthy result for this unit. Type=oneshot with RemainAfterExit=yes correct here because docker compose up -d return as soon as e start the container: systemd dey track say the stack don come up, no be foreground process. If you write Type=simple instead, systemd go see say the command don exit immediately, mark the service as dead, then either give up or enter restart loop, depending on your Restart= setting. For native install, the project get its own equivalent, kirocrew service install, wey write /etc/systemd/system/kirocrew.service and run the gateway as your user. No run both units. You fit read the broader explanation for systemd services and timers for VPS.
First run: sign in and collect dashboard token
Container go start the gateway, but agent runtime never sign in yet. Sign in inside the container:
docker exec -it kirocrew kiro-cli loginThat go print device code and URL wey you go open for your own browser. Then create dashboard token:
docker exec kirocrew kirocrew token --ttl 2hDashboard URL na http://localhost:5476/?token=<the token>. Tokens dey expire: session dey last one hour by default, and documented maximum na twenty hours. If dashboard open blank or e dey send you straight back outside, token don usually expire, so create another one. Never paste token inside ticket or chat message, because anybody wey get am get your agent.
Access the dashboard through SSH, and never publish port 5476
Look again at the bind address for the project example: -p 127.0.0.1:5476:5476. Inside the container, the gateway dey listen on 0.0.0.0, because e must dey reachable through the port mapping. But the mapping itself publish only to loopback for the host. Remove the 127.0.0.1: prefix, and the gateway go dey for public internet for anybody wey scan that port. Firewall rule no go save you too: Docker publish ports by writing DNAT rules wey dem evaluate before ufw filtering, so ufw deny 5476 no dey affect published port. Docker ports wey bypass ufw explain how this mechanism dey work.
Forward the port through SSH from your laptop:
ssh -N -L 5476:127.0.0.1:5476 you@your-server.example.comLeave that command running, then open http://localhost:5476/?token=<the token> locally. To make the forward happen automatically for every connection, put am for ~/.ssh/config:
Host your-server.example.com
LocalForward 5476 127.0.0.1:5476If port 5476 dey already in use for your laptop, change only the number for the left side: ssh -N -L 45476:127.0.0.1:5476 you@your-server.example.com, then browse to http://localhost:45476/?token=....
One documented behaviour wey you should expect through a tunnel be say: the gateway dey read forwarded requests as remote requests, so config-write and secret-reveal endpoints for the dashboard go reject dem. If settings change no save through SSH, na this be the reason; e no be bug. Edit the config for the host instead:
docker cp kirocrew:/home/kirocrew/.kiro/crew/config.json .
# edit config.json here
docker cp config.json kirocrew:/home/kirocrew/.kiro/crew/config.json
docker exec -u 0 kirocrew chown kirocrew:kirocrew /home/kirocrew/.kiro/crew/config.json
docker restart kirocrewFor phone access, the project point to Tailscale's tailscale serve, wey keep the dashboard inside your own tailnet instead of public hostname. Prefer this one over public reverse proxy. The token dey travel inside the URL, and every access log for the request path go write that URL.
Give the agent the smallest blast radius wey you fit
The container dey check for sandbox support when e start for the first time, and the result dey decide whether agents fit execute anything. If namespace isolation dey available, agent subprocesses go run isolated. If e no dey available and KIROCREW_ALLOW_UNSANDBOXED=1 no dey set, execution go refuse instead of running unconfined, so gateway wey look healthy while every task dey stall na usually this issue. The decision dey for docker logs kirocrew from that first run. The project still publish one seccomp (secure computing mode) profile wey you fit apply:
curl -fsSL https://raw.githubusercontent.com/kirodotdev/KiroCrew/main/docker/seccomp/kirocrew-seccomp.json \
-o /opt/kirocrew/kirocrew-seccomp.json security_opt:
- seccomp:./kirocrew-seccomp.jsonIf you set KIROCREW_ALLOW_UNSANDBOXED=1, make you understand wetin change: the container na now the only boundary between the agent and your server. The project warning important make we repeat am complete. No mount host paths wey you no go hand directly to the agent. For real use, that one rule out the Docker socket, any bind mount of /, and any directory wey hold another service data.
The remaining part na the framework wey apply to every agent wey get permission to run commands. Limit the credentials to the one repository or the one bucket wey e need. Never use personal token wey get account-wide rights. Run am as dedicated user wey home directory no hold anything else. Na wetin least privilege users for one VPS dey for. When the agent write code and later run that code, give am machine wey e fit spoil: disposable VM for coding agents na stronger boundary pass any flag for this compose file, because you fit delete am instead of cleaning am. The same reasoning shape running OpenClaw safely for one VPS and self-hosting the Hermes agent for one VPS. Tools count as blast radius too: if you give the agent web search, every page wey e fetch become untrusted input. So pointing am to your own SearXNG instance na prompt injection decision as much as na plumbing decision. Scheduled work still dey spend money while you dey sleep, because inference bills dey charge your Kiro plan. So set the limits wey controlling wetin AI agent cost for one VPS describe before you add nightly job.
Back up state volume before every upgrade
First find the real volume name. Compose dey add project name before named volumes. By default, e dey use directory name. So the volume wey dem declare as kirocrew-home for /opt/kirocrew/compose.yaml go create as kirocrew_kirocrew-home:
docker volume lsStop gateway before you copy anything. memory.db and memory_index.db na SQLite databases. If you copy database while dem still dey write, you fit capture half-written transaction. When you restore am, the file fit corrupt. The project's migration instructions talk the same thing: only move memory after you stop the gateways.
sudo systemctl stop kirocrew
docker run --rm -v kirocrew_kirocrew-home:/data:ro -v "$PWD":/backup \
alpine tar czf /backup/kirocrew-2026-08-06.tgz -C /data .
sudo systemctl start kirocrewCopy the archive comot from the server. To restore am, use the same command, but make sure say container dey stopped and use tar xzf instead of tar czf:
sudo systemctl stop kirocrew
docker run --rm -v kirocrew_kirocrew-home:/data -v "$PWD":/backup \
alpine tar xzf /backup/kirocrew-2026-08-06.tgz -C /data
sudo systemctl start kirocrewMoving go new host different from restoring for the same host. The project give clear instruction about this. Chat history and project notes under workspace/memory/ go carry over. The two database files and config.json go carry over too. PID files, security event log, and .env belong to the old host, so leave dem there. Enter the secrets again for the new server.
How you fit roll back bad upgrade
Upgrade na short work, and e safe only because you pin version. Take backup first, then change the tag:
sudo systemctl stop kirocrew
# take the backup here, as above
sudo nano /opt/kirocrew/compose.yaml # set the new image tag
sudo systemctl start kirocrew
docker compose -f /opt/kirocrew/compose.yaml ps
curl -s http://127.0.0.1:5476/api/healthdocker compose up -d go pull the image if e never dey for the box already, so na only tag edit be the whole upgrade. To roll back na the same steps with the old number, and e go give you the exact image wey you get before, because version tags no dey change.
The binary go roll back cleanly. Na the state fit get problem. Newer gateway fit rewrite config.json or migrate the memory databases enter format wey older gateway no fit read, and as of August 2026, nobody don document downgrade path. So if the older image start and begin behave strangely, no debug am. Stop am, restore the backup wey you take before the upgrade, then start again. Na this be the main reason backup dey come first, and na why the habit of upgrading now and backing up later no work for project wey still young.
Wetin no get proof here
Make you honest about how new this software be. Version 0.1.3 just come out few days ago when dem write this, and the release notes na automated changelog links instead of migration notes. There no upgrade history yet. Nothing for this guide don prove long-term. So measure memory growth, database size, and scheduler reliability for your own box instead of assuming say dem go behave well.
You suppose test 2 behaviours by yourself before you depend on dem. First, check whether downgrade fit read state wey newer version write. Try am with copy of the volume when mistake no matter, not during outage. Second, check wetin the gateway go do when Kiro sign-in expire while scheduled job dey due. These na the kind rough edges wey young project fit quietly fix between releases. Both easy to check now.
FAQ
Why KiroCrew dashboard no dey open for my server public IP?
Na because the published example bind the port to loopback. -p 127.0.0.1:5476:5476 map the container port only to the host loopback address, and na deliberate. Reach am by forwarding the port through SSH with ssh -N -L 5476:127.0.0.1:5476 you@your-server, then open http://localhost:5476/?token=<token> for your laptop. If you remove the 127.0.0.1: prefix to make am reachable, you go put the gateway for public internet. Firewall rule no go contain am, because Docker published-port DNAT rules dey evaluate before ufw filter the traffic.
Where KiroCrew dey store its data, and wetin I suppose back up?
Everything dey under ~/.kiro/crew, wey be /home/kirocrew/.kiro/crew inside the container image, and KIROCREW_HOME dey relocate am. Back up the whole directory, or the whole Docker volume, when the gateway don stop. memory.db and memory_index.db na SQLite databases, so copy wey you take while the gateway dey write fit no consistent. When you move go new host, workspace/memory/, the two database files and config.json go carry over, while PID files, the security event log, and .env belong to the old host.
I suppose use the stable tag or version tag?
Use version tag. stable dey change every time release ships, so the version wey you dey run fit change under you for the next pull, and the tag itself no tell you wetin dey run. Version tags like 0.1.3 no dey change, and na this one make rollback work: put the old number back and get the identical image. As of 6 August 2026, the newest release na 0.1.3.
Why my agent dey refuse to run any commands?
The container dey check for sandbox support the first time e start. If e no fit isolate agent subprocesses and KIROCREW_ALLOW_UNSANDBOXED=1 no dey set, e go refuse to execute dem instead of running dem without isolation. Because of this, the gateway go look healthy while every task dey stall. docker logs kirocrew show the sandbox decision from that first run. If you set the variable, the container become the only boundary between the agent and the host. So, mount nothing wey you no go give the agent directly.
I need Kiro account to self-host KiroCrew?
Yes, as of August 2026. KiroCrew na free software under Apache 2.0, but e dey drive kiro-cli, wey require one-time sign-in, and Kiro plan dey bill agent inference. Inside the container, run docker exec -it kirocrew kiro-cli login and approve the device code for your browser. Until that sign-in complete, the gateway go start and dashboard go load, but the agent no get model to talk to.