How to Self-Host KiroCrew on VPS 24/7
Learn how to run KiroCrew as a pinned Docker container with systemd, SSH, backups and rollback, so memory and scheduled jobs survive VPS reboots.
Why you go 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, and e reload all of dem when process restart. None of dis go help if process no dey run for 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, and e first public releases come out for early August 2026. One process, wey dem call gateway, dey own the state and serve web dashboard for port 5476. You fit reach that gateway from dashboard, from the kirocrew CLI, or from chat channel like Slack. Gateway na the only thing you dey self-host, so this guide dey focus on keeping am alive, keeping am away from public internet, and making sure say you fit restore am after bad upgrade.
Get two things for mind before you start. KiroCrew dey drive kiro-cli, wey need one-time sign-in with Kiro account, and Kiro plan dey bill agent inference, so as of October 2026 this no be offline setup. The project still young and e dey move fast: the newest stable release move from 0.1.3 on 5 August 2026 to 0.8.0 on 9 October 2026. Assume say you go need roll back sometime, and install am in a way wey go let you do that. If you never run agent for server before, running coding agent for VPS explain the basic rules wey this guide dey build on. If agent side newer to you pass server side, learning wetin agent loop, im tools and im memory really be first go make the choices below look like decisions instead of incantations.
Wetín KiroCrew need, and where e state dey
Native install need Python 3.12 or newer, Node.js 22.12.0 or newer if you build the dashboard from source (the project recommend Node 24 LTS), and kiro-cli, wey you install according to its own docs and sign in with kiro-cli login. Container install no need any of these things for the host. E need Docker. Na the main reason to prefer am.
State dey inside ~/.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.security_events.jsonl: 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 actually dey load the box na whatever the agent run, 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 during the first months of a project. Compare am with runtime wey give every worker im own container and im own browser, where self-hosting OpenBot's AI coworkers make RAM be the first sizing question before disk.
Which one of the three install paths you suppose use
The project dey 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.8.0Dem 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 just dey run am.
Use the container. Native install dey 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 undo the changes by hand. The container keep the runtime inside one image and the state inside one volume. This one make rollback just a matter of changing the tag and restarting.
Pin the image to a release tag, no be na stable
The project own example 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, and latest too, wey dey point to the same release. E dey point to whichever stable release be the newest one, so the next pull fit change the version wey you dey run without you choosing am, and the tag no record anything about which version dat one be. Version tags no dey change, so pin one. The newest stable release as of 12 October 2026 na 0.8.0, wey dem publish on 9 October 2026. insider and nightly tags dey too, and for project wey still young like this, nightly mean say the code change this morning.
Write /opt/kirocrew/compose.yaml:
services:
kirocrew:
image: ghcr.io/kirodotdev/kirocrew:0.8.0
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 dey also use for its 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, and /api/health dey answer without token (same thing apply to /api/live and /api/ready, na wetin make dem useful as probes). If the state remain starting, read docker logs kirocrew before you change anything. The first run dey download the embedding model, so slow link fit make the first start take long.
Make e continue with systemd
restart: unless-stopped dey bring the container back after crash and reboot, as long as Docker itself dey 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. This na how KiroCrew take arrange am, 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). Na the healthy result for this unit. Type=oneshot with RemainAfterExit=yes correct here because docker compose up -d dey return immediately after 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 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 ship 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 on a VPS. Unit wey fail to come back go remain silent unless you make am report the problem. So add an OnFailure= handler wey send alert go your own ntfy server, and you go hear say the gateway dey down from your phone, instead of finding out through scheduled job wey never run.
First run: sign in and collect dashboard token
Container go start gateway, but agent runtime never sign in yet. Sign in inside container:
docker exec -it kirocrew kiro-cli loginThat one go print device code and URL wey you go open for your own browser. Then mint dashboard token:
docker exec kirocrew kirocrew token --ttl 2hDashboard URL na http://localhost:5476/?token=<the token>. Tokens dey expire: you must open the printed link within five minutes after minting am, the session wey e open dey default to one hour, and documented maximum na twenty hours. If dashboard load blank or e send you straight back, na usually expired token cause am, so mint another one and open am immediately. Never paste token for ticket or chat message, because anybody wey hold am dey hold 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 port mapping need reach am, but the mapping itself publish only to loopback for the host. Delete the 127.0.0.1: prefix, and the gateway go dey on public internet for anybody wey scan that port. A ufw rule no go save you too: Docker publish ports by writing DNAT rules wey route traffic before ufw rules apply, so ufw deny 5476 no dey do anything to published port. Docker own solution na to put filtering rules for its DOCKER-USER chain. E dey work, but those rules dey see the container internal address and port unless you match with the conntrack extension. So, loopback bind remain the simpler control and the one wey hard to misconfigure. Docker ports wey bypass ufw explain how this mechanism work.
Forward the port over SSH from your laptop instead:
ssh -N -L 5476:127.0.0.1:5476 you@your-server.example.comLeave am 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 don already dey 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 open http://localhost:45476/?token=... for your browser. You go dey stack forwards like this once another agent share the same box, because self-hosting open-kritt for security scanning put another loopback-only dashboard for the same server, on port 5173.
Two documented behaviours decide which settings you fit change from the dashboard. First, the gateway treat any request wey carry forwarding headers (Forwarded, X-Forwarded-* or X-Real-IP) as remote, and its config-write and secret-reveal endpoints reject those requests. Reverse proxy dey add those headers; plain ssh -L forward no add any, so e look local and the gateway no reject am for that reason. Second, for the container image, channel-credential pages and secret-revealing views dey read-only for any browser wey no be direct-local, and the image no get text editor. If channel credential no gree save from the dashboard, na this be the reason, no be bug. Edit the config on 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 over public reverse proxy. The token dey travel inside the URL, and every access log along the path go write the URL. This rule concern wetin dey behind the port, not the port itself: something like Halcyon, wey rebuild Jellyfin library as browsable 90s video store dey meant for other people to open and e fit be good candidate for reverse proxy, but gateway wey fit run commands on your server no be.
Give the agent the smallest blast radius you can
Container dey check whether sandbox support dey available the first time e start, and this result 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 set, execution go refuse instead of running unconfined. So, if gateway dey look healthy but every task dey stall, na usually this be the cause. The decision dey inside docker logs kirocrew from that first run. The project also publish a 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.jsonFor host wey AppArmor dey enabled, the project's Docker guide talk say applying the seccomp profile na only half of the change. Docker also attach its docker-default AppArmor profile, and e pair the profile with apparmor=unconfined. That option only remove AppArmor's per-container rules. The seccomp profile, the dropped capabilities, and the non-root user still dey apply.
If you set KIROCREW_ALLOW_UNSANDBOXED=1, understand exactly wetin change: na the container alone remain as boundary between the agent and your server. The project's warning worth repeating complete. No mount host paths wey you no go hand directly to the agent. For practice, this rule out the Docker socket, any bind mount of /, and any directory wey hold another service's data.
The rest na the security frame wey apply to every agent wey get permission to run commands. Limit its 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 whose home directory no hold anything else. Na this least privilege users on a VPS dey for. When agent write code and then run that code, give am machine wey e get permission to break: a disposable VM for coding agents na stronger boundary than any flag for this compose file, because you fit delete am instead of cleaning am. This same reasoning shape running OpenClaw safely on a VPS and self-hosting the Hermes agent on a VPS. Tools count as part of blast radius too. If you give agent web search, every page wey e fetch become untrusted input. So pointing it at your own SearXNG instance na prompt injection decision as much as na plumbing decision. Scheduled work fit also spend money while you sleep, because inference dey bill to your Kiro plan. Set the limits wey controlling what an AI agent costs on a 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 you declare as kirocrew-home for /opt/kirocrew/compose.yaml go create as kirocrew_kirocrew-home:
docker volume lsStop the gateway before you copy anything. memory.db and memory_index.db na SQLite databases. If you copy database while e dey write, you fit capture transaction wey never finish. When you restore am, the file fit corrupt. The project migration instructions talk the same thing: only move memory after you stop the gateways. This stop-first rule no be only for KiroCrew. If photo server dey share the same box, the PhotoPrism and Immich comparison get the exact backup commands wey each one need.
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 box. To restore am, use the same command while the container dey stopped, and put tar xzf where tar czf dey:
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 no be the same work as restoring for the same host. The project instructions clear about this. Chat history and project notes inside workspace/memory/ go carry over. The two database files and config.json go carry over too. PID files, security event log, and .env dey tied to the old host, so leave dem there and enter the secrets again for the new box.
Sabi how you go roll back bad upgrade
Upgrade dey short, and e safe only because you pin one version. First take backup, 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 no dey already for the box, so na the tag edit be the complete upgrade. Rollback na the same sequence 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 no documented downgrade path for state dey available as of 12 October 2026. The install guide talk one case clearly: data wey move comot from legacy ~/.kirocrew home no get rollback, and release wey older than ~/.kiro/crew go start empty. So if the older image start and then dey behave strangely, no debug am. Stop am, restore the backup wey you take before the upgrade, then start again. Na this be the whole reason backup dey come first, and na why the habit of upgrading now and backing up later dey fail for project wey still young.
Wetin no prove for here
Make we honest about how young this software still be. Version 0.1.3 bin na only days old when dem first write this guide for August 2026, and by 12 October 2026 the stable line don reach 0.8.0. Most release notes still be automated changelogs; the 0.8.0 notes na people write by hand, but dem no get migration section, and the upgrade track record na about two months old. Nothing for this guide na long-term result, so treat memory growth, database size, and scheduler reliability as things wey you need measure for your own box, instead of things wey you suppose assume.
Two behaviours deserve make you test dem yourself before you depend on dem. First, check whether downgrade fit read state wey newer version write: try am for copy of the volume while e no matter, no be during outage. Second, check wetin gateway go do when Kiro sign-in expire while scheduled job dey due. Both na the kind rough edge wey young project dey quietly fix between releases, and both cheap 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 so dem intentionally set am. Forward the port over 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 so e fit reach from outside, you go put the gateway for public internet. ufw no go contain am because Docker published-port DNAT rules route the traffic before ufw rules apply. Na only rules for Docker DOCKER-USER chain dey filter published ports. Keeping the loopback bind dey simpler.
Where KiroCrew dey store its data, and wetin I suppose back up?
Everything dey under ~/.kiro/crew. Inside the container image, na /home/kirocrew/.kiro/crew, and KIROCREW_HOME dey relocate am. Back up the whole directory or the whole Docker volume while the gateway dey stopped. memory.db and memory_index.db na SQLite databases, so copy wey you take while the gateway dey write fit get inconsistent data. When you move to new host, workspace/memory/, the two database files, and config.json go move with am. 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 after the next pull, and the tag itself no tell you wetin dey run. Version tags like 0.8.0 no dey change. Na this make rollback work: put the old number back and you go get the identical image. As of 12 October 2026, the newest stable release na 0.8.0.
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 only things wey you go give the agent directly.
I need Kiro account to self-host KiroCrew?
Yes, as of October 2026. KiroCrew na free software under Apache 2.0, but e dey drive kiro-cli. This one need one-time sign-in, and agent inference dey billed to Kiro plan. Inside the container, run docker exec -it kirocrew kiro-cli login and approve the device code for your browser. Until the sign-in finish, the gateway go start and the dashboard go load, but for the default harness the agent no get model to talk to. The project custom backend guide allow a claude harness send the main model turns to any Anthropic-compatible endpoint wey you choose. But kiro-cli sign-in still remain necessary for Kiro-specific features.