---
title: "Deployment"
description: "Run mikan under PM2 with persistent state, managed sandbox images, graceful shutdown, and an optional health endpoint."
url: "https://geminixiang.github.io/deployment/"
---

# Deployment

PM2 daemonizes mikan, restarts it after failures, retains stdout/stderr logs, and can start it
    on boot.
    The mikan host process uses Docker to create per-conversation tool containers from the selected
    sandbox image.
    Keep one host-only state directory for settings, session-independent runtime state, vaults, and
    GitHub polling watermarks.

## Prerequisites

- Node.js `>=22.19.0`
- at least one complete Slack, Telegram, Discord, or GitHub credential set
- a persistent state directory (default `~/.mikan`); the working directory defaults to `<state-dir>/workspace`
- Docker available to the PM2 user when using `image:*` or `container:*`

The published `mikan-sandbox` image is a tool runtime, not a standalone mikan server image. PM2 runs `mikan` on the host; image mode creates and manages containers through the host Docker daemon.

## PM2 deployment

1. Install mikan and PM2:

   ```bash
   npm i -g @geminixiang/mikan pm2
   ```

2. Create the global settings file in the exact state directory the service will use:

   ```bash
   mkdir -p /srv/mikan/workspace
   mikan --onboard --state-dir=/srv/mikan/state
   ```

   Review `/srv/mikan/state/settings.json`. The directory must be owned by the PM2 process user and
   must not be world-writable; `chmod 0700 /srv/mikan/state` is recommended.

3. Create the secrets file. All platform tokens, provider keys, and other secrets live in
   `~/.mikan/mikan.env` — mode `0600`, outside any repo tree — and the ecosystem file loads it at
   start. Never put secrets in the ecosystem file or PM2's own environment:

   ```bash
   curl -o ~/.mikan/mikan.env https://raw.githubusercontent.com/geminixiang/mikan/main/deploy/pm2/mikan.env.example
   chmod 600 ~/.mikan/mikan.env
   ```

   Fill in at least one complete platform credential set plus an LLM provider key. Run `mikan env`
   to see the full annotated inventory and what is currently set.

4. Pull the managed sandbox image:

   ```bash
   docker pull ghcr.io/geminixiang/mikan-sandbox:latest
   ```

   Do not also start a long-lived container when the selected mode is `image:*`. A pre-existing
   container is needed only for `container:<name>` mode.

5. Download the ecosystem file:

   ```bash
   curl -O https://raw.githubusercontent.com/geminixiang/mikan/main/deploy/pm2/ecosystem.config.cjs
   ```

   The ecosystem file is supervision-only: process name, restart policy, graceful-shutdown timeout,
   and the `mikan.env` loader. Behavior (model, sandbox limits, reply modes) stays in
   `<state-dir>/settings.json`.

6. Edit `args` to use absolute paths and one sandbox mode (run `mikan --help` for the flag
   reference):

   ```js
   args: "--state-dir=/srv/mikan/state --sandbox=image:ghcr.io/geminixiang/mikan-sandbox:latest /srv/mikan/workspace",
   ```

   The working-directory argument is optional; without it mikan uses `<state-dir>/workspace` and
   creates it on first start.

7. Start and persist the process:

   ```bash
   pm2 start ecosystem.config.cjs
   pm2 save
   pm2 startup
   ```

8. Run the command printed by `pm2 startup` to enable boot startup.

Relative workspace paths are resolved from PM2's current working directory. Absolute paths avoid silently opening a different workspace after service migration or reboot.

## Upgrade

```bash
npm i -g @geminixiang/mikan
pm2 reload mikan
```

After editing `~/.mikan/mikan.env`, reload through the ecosystem file so the environment is
re-read:

```bash
pm2 reload ecosystem.config.cjs
```

  The included PM2 configuration waits up to 60 seconds after SIGTERM before SIGKILL. mikan first
  drains the conversation runtime, stops the events watcher, and gives Sentry up to five seconds to
  flush. Runs that exceed the supervisor timeout can still be terminated.

## Health endpoint

The portal server exists only when `LINK_PORT` is set, or when `LINK_URL` causes the default port `8181` to be used:

```bash
curl http://127.0.0.1:8181/health
# {"ok":true}
```

`/health` is a liveness check for the HTTP process only. It does not verify platform connections, Docker, sandbox provisioning, LLM providers, or event delivery, so do not use it as the only readiness signal.

See the [maintained PM2 ecosystem file on GitHub](https://github.com/geminixiang/mikan/blob/main/deploy/pm2/ecosystem.config.cjs) and [Sandbox modes](/sandbox/) for mode-specific requirements.
