v2.0.0next
Run the server

Run Kraft as a service

Start Kraft at login and restart it if it exits, set its environment, stop or remove the service, and run a second instance beside the first.

Kraft starts at login and starts again if it exits, and kraft admin health shows it running. A second instance with its own home and port runs beside it.

Before you start

  • $KRAFT_HOME is ~/.kraft unless you set it; see Environment variables.
  • Run kraft admin install-service from a shell where claude, git, gh and your other tools are on PATH. The service takes its PATH from that shell.
  • Kraft must not be running: install-service refuses while a server is up.

Run Kraft as a service

A service starts Kraft at login and restarts it if it exits.

  1. Stop the running server:
    kraft admin stop
    
  2. Install the service:
    kraft admin install-service
    

    On macOS this writes a launchd agent and loads it. On Linux it writes a systemd user unit and enables it. It prints where it wrote the file:
    kraft: wrote /Users/you/Library/LaunchAgents/com.kraft.daemon.plist, loaded with launchctl
    

    On Linux the line names ~/.config/systemd/user/kraft.service (under $XDG_CONFIG_HOME when you set it) and systemctl --user.
  3. Check the service, as Verify the service shows.

There is one service per user. Its launchd label and systemd unit name are fixed, so run a second instance by hand: see Run two instances.

Give the service an environment

install-service copies PATH and the variables that locate the instance from the shell you ran it from, and writes them into the unit. The list is in Environment variables. To pick up a changed PATH later, run kraft admin uninstall-service, then install-service again.

Nothing else from that shell reaches the service. A repo's env_passthrough copies a variable from the server's own environment, so under a service it finds only the ones install-service copied. A key you export in your shell, such as GEMINI_API_KEY or a forge token, does not reach the workers. Set it in the service instead, then run kraft admin restart:

  • systemd: run systemctl --user edit kraft and add the variable to the drop-in it opens:
    [Service]
    Environment="GEMINI_API_KEY=..."
    

    The drop-in is a separate file from the unit Kraft writes, so it survives uninstall-service and install-service.
  • launchd: add it to the agent's EnvironmentVariables:
    plutil -replace EnvironmentVariables.GEMINI_API_KEY -string "${GEMINI_API_KEY:?export GEMINI_API_KEY in this shell first}" \
      ~/Library/LaunchAgents/com.kraft.daemon.plist
    

    -replace adds the key or changes it, so it is safe to run again. With the variable unset or empty in your shell, the shell stops the command and prints that message, so an empty value never replaces the key already in the file. install-service rewrites that file, so do this again after you reinstall the service.

Both files are usually readable by other accounts on the machine, and so is a key written into them. On systemd, keep keys in a file only you can read, such as ~/.config/kraft/secrets.env after chmod 600, and point the drop-in at it with EnvironmentFile=%h/.config/kraft/secrets.env instead of Environment=.

Verify the service

kraft admin restart
kraft admin health

restart goes through the service manager when a service is installed, and prints:

kraft: restarted the service

health exits 0 once the server is back, and its version row names the release it runs:

status         ok
version        2.0.0

After an update and before the restart, health also prints the installed release on an installed row that says restart pending. A 1.4 server shows no such row: Upgrade from 1.4 says what to read instead.

Stop a service

kraft admin stop does not keep a service stopped: the service manager starts it again. Where a guide says to stop the server and Kraft runs as a service, remove the service instead, then put it back when you are done.

Before you remove it, save what you added to the service. uninstall-service deletes the service file, and install-service writes a new one from your shell:

  • launchd: both commands replace the agent's plist, so every key you added to its EnvironmentVariables with plutil is gone, a forge token or worker credentials included, and nothing says so. Before uninstall-service, run plutil -p ~/Library/LaunchAgents/com.kraft.daemon.plist and note the keys you added. After install-service, add them again, as Give the service an environment shows, then run kraft admin restart.
  • systemd: a drop-in is a separate file from the unit Kraft writes, so it survives. Only what you put in the unit file itself is lost.

Then remove the service:

kraft admin uninstall-service
kraft admin stop

uninstall-service does not wait for the server to finish, so kraft admin stop follows it. It prints kraft: no server running, or kraft: stopped (pid N) when the server was still draining: go on after either.

Do the task that needs the server stopped. Then put the service back:

kraft admin install-service

install-service takes the environment of the shell you run it from, as above.

Remove the service

Save what you added to the service first, as Stop a service says: removing a launchd agent deletes its file.

kraft admin uninstall-service

It stops the server, without waiting for it to finish, and deletes the launchd agent or the systemd unit. A systemd drop-in you made stays.

Run two instances

Two instances need separate homes and separate ports. A second server on the same port refuses to start and names the one already there.

  1. Start the second instance once on a free port. The first start seeds its config/. This example uses 8767, since 8765 is the default and 8766 is where a contributor's just dev runs:
    KRAFT_HOME=~/kraft-staging KRAFT_PORT=8767 kraft admin start --detach
    

    You can also make config/ yourself before the first start, to write its access.yaml or a sandbox.yaml first. The first start then adds each default file the directory lacks, and keeps the files you wrote.
  2. Write the port into that home's access.yaml, so the server and the kraft command find it without KRAFT_PORT. A port given only as KRAFT_PORT or --port is not saved, and every other command would still dial 8765.
    The command below adds the line only if access.yaml has no port: line yet. Kraft does not seed access.yaml, so a missing file counts as no port: line. A second run changes nothing. If the file already has a port: line, change its value to 8767 by hand: the command leaves it alone.
    f=~/kraft-staging/config/access.yaml
    grep -q '^port:' "$f" || printf '\nport: 8767\n' >> "$f"
    
  3. Run every command for that instance with the same KRAFT_HOME:
    KRAFT_HOME=~/kraft-staging kraft view list
    KRAFT_HOME=~/kraft-staging kraft admin stop
    
  4. Start any agent you run yourself with the same KRAFT_HOME. An agent reaches the instance its environment names. Workers inherit their instance's KRAFT_HOME, so their own kraft calls reach the instance that started them.

You know it worked when step 1 prints the address with port 8767, and KRAFT_HOME=~/kraft-staging kraft view list shows the second instance's board, not the first's.

Next steps

Copyright © 2026