> ## Documentation Index
> Fetch the complete documentation index at: https://comis-fix-skill-import-vetting-gate.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Install on Render

> Deploy Comis to Render.com from Docker Hub — managed Linux host, automatic HTTPS, persistent storage

This guide deploys Comis to [Render.com](https://render.com) using the published
Docker image. Render provides a managed Linux container, HTTPS endpoint, and
optional persistent storage. Do not assume the exec sandbox is active merely
because the host is Linux: confirm the startup diagnostics and sandbox provider
on the deployed service.

<Info>
  You don't need to understand the technical details to use this feature. The configuration examples below are copy-paste ready.
</Info>

<Tip>
  The same general pattern
  works on Fly.io, Railway, DigitalOcean App Platform, and AWS ECS — adjust the
  service-creation flow but keep the environment variables and disk mount the same.
</Tip>

## Before you start

| Need                                                         | How to get it                                                                                                               |
| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| **Render account**                                           | [Sign up](https://render.com)                                                                                               |
| **Hosted model credential**                                  | Obtain one from a supported provider; interactive OAuth/local providers are better suited to a host you can access directly |
| **Channel tokens** *(optional)*                              | Telegram BotFather, Discord Developer Portal, Slack app, etc.                                                               |
| **A 32-byte hex string**                                     | Run `openssl rand -hex 32` locally — you'll use this as the gateway bearer token                                            |
| **A Render plan with persistent disk and always-on service** | Check Render's current plan limits before creating the service                                                              |

You **don't** need Docker, Node.js, or anything else installed on your machine.
Render pulls the image directly from Docker Hub.

<Warning>
  **Plan choice.** Select a current Render plan that provides persistent storage
  and the availability your channels require. Without a disk, redeploys can lose
  configuration, credentials, memory, and sessions. A sleeping service cannot
  respond to channel events while suspended. Render's pricing and limits can
  change; verify them in the Render dashboard.
</Warning>

## Deploy from the dashboard

<Steps>
  <Step title="Create a new Web Service">
    Log into Render → click **New +** in the top right → **Web Service**.

    On the next screen, look for the **"Existing Image"** option (not "Public Git Repository"
    — we're deploying a prebuilt image, not source). Click it.
  </Step>

  <Step title="Point at the Comis Docker Hub image">
    In the **Image URL** field, paste:

    ```
    docker.io/comisai/comis:latest-slim
    ```

    <Tip>
      For production, pin to an immutable version tag instead of `latest-slim` so
      your service doesn't update under you. Find the latest version on the
      [Comis Docker Hub page](https://hub.docker.com/r/comisai/comis/tags) and use
      a tag such as `docker.io/comisai/comis:<version>-slim`.
    </Tip>

    Click **Connect** (or "Next" depending on your Render UI version).
  </Step>

  <Step title="Configure the service">
    Fill in:

    | Field                              | Value                                                     |
    | ---------------------------------- | --------------------------------------------------------- |
    | **Name**                           | `comis` (or anything you like — becomes part of your URL) |
    | **Region**                         | Pick the one closest to you/your users                    |
    | **Instance Type**                  | A plan with persistent disk and suitable availability     |
    | **Branch / Tag**                   | leave blank (we're using a tag-pinned image)              |
    | **Dockerfile Path**                | leave blank                                               |
    | **Docker Build Context Directory** | leave blank                                               |

    Render will detect the image's exposed port. Comis exposes **4766**.
  </Step>

  <Step title="Add environment variables">
    Scroll down to **Environment Variables** and add each of these. Use the
    "Add Secret File" / "Add" button for each row. Mark each as a **Secret**
    (lock icon) so the values stay hidden in logs and the dashboard.

    **Required:**

    ```bash theme={}
    COMIS_GATEWAY_HOST=0.0.0.0
    COMIS_GATEWAY_PORT=4766
    COMIS_GATEWAY_TOKEN=<paste the openssl rand -hex 32 output here>
    ANTHROPIC_API_KEY=<your-provider-key>
    ```

    **Optional channel tokens** — add only those you'll use:

    ```bash theme={}
    TELEGRAM_BOT_TOKEN=<your-bot-token>
    DISCORD_BOT_TOKEN=<your-bot-token>
    SLACK_BOT_TOKEN=<your-bot-token>
    OPENAI_API_KEY=<your-provider-key>
    ```

    <Warning>
      Never store API keys, tokens, or passwords directly in `config.yaml`. Use the `.env` file or [Secret Manager](/security/secrets) for credential management.
    </Warning>

    <Tip>
      The encrypted secrets store is **enabled by default**. On first boot, Comis
      auto-generates a master key and writes it to `~/.comis/.env` (mode 0600).
      Back up `SECRETS_MASTER_KEY` separately in a secure credential manager —
      losing it makes `secrets.db` permanently unreadable. To disable the encrypted store, set
      `security.storage: env` (or `file`) in `config.yaml`.
    </Tip>
  </Step>

  <Step title="Add a persistent disk">
    Still on the create-service screen, scroll to **Advanced** → expand it →
    look for **Add Disk**. Configure:

    | Field          | Value                          |
    | -------------- | ------------------------------ |
    | **Name**       | `comis-data`                   |
    | **Mount Path** | `/home/comis/.comis`           |
    | **Size**       | `1 GB` (you can grow it later) |

    This is where your agent's memory database, logs, secrets store, and
    workspace live. **Without this disk, every redeploy wipes your data.**
  </Step>

  <Step title="Set the health check">
    Scroll to **Health Check Path** and enter:

    ```
    /health
    ```

    Render uses this to decide when traffic should start flowing to your
    container after a deploy. Comis's daemon serves a JSON `{"status":"ok",...}`
    on this endpoint as soon as the gateway is up.
  </Step>

  <Step title="Deploy">
    Click **Create Web Service** at the bottom. Render starts pulling the
    image, attaching the disk, and starting the container. Deployment duration
    depends on image-pull and platform capacity.

    Watch the **Logs** tab — you should eventually see:

    ```json theme={}
    {"level":30,"name":"comis-daemon","msg":"Comis daemon started", ...}
    ```

    When the service status switches to **Live** at the top of the page,
    you're done.
  </Step>
</Steps>

## Verify the deployment

Render gives your service a public URL like `https://comis-XXXX.onrender.com`.
Find it on the service's dashboard page (top of the screen, next to the green
"Live" badge).

Run these from any terminal — they need no Docker or Comis CLI locally:

```bash theme={}
# Replace with your actual values:
RENDER_URL=https://comis-XXXX.onrender.com
GATEWAY_TOKEN=<the one you generated with openssl rand -hex 32>

# 1. Health check (no auth needed)
curl -sf $RENDER_URL/health
# → {"status":"ok","timestamp":"...","instanceId":"..."}

# 2. Auth check — should return 401
curl -s -o /dev/null -w "%{http_code}\n" $RENDER_URL/v1/chat/completions \
  -X POST -H "Content-Type: application/json" \
  -d '{"model":"default","messages":[{"role":"user","content":"hi"}]}'
# → 401

# 3. Real chat round-trip
curl -sS -X POST $RENDER_URL/v1/chat/completions \
  -H "Authorization: Bearer $GATEWAY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "default",
    "messages": [{"role":"user","content":"Reply with exactly: hello-from-render"}]
  }' | jq -r '.choices[0].message.content'
# → hello-from-render
```

If all three of those work, you have a fully functional Comis deployment.

## Open the dashboard

In your browser, go to:

```
https://comis-XXXX.onrender.com/
```

It redirects to `/app/` and prompts for your gateway token. Paste the same
`COMIS_GATEWAY_TOKEN` you set in the env vars. From here you can:

* Add agents, configure their model and policies
* Connect channels (Telegram, Discord, Slack, etc.) — paste each bot token
* Watch live logs, traces, and memory contents
* Edit configuration through the UI (writes to `/home/comis/.comis/config.yaml`
  on the persistent disk)

<Tip>
  **Bringing an existing config from local testing.** If you've been testing
  Comis locally, you already have a `~/.comis/config.yaml` with agents and
  channels configured. The simplest way to seed your Render deployment with
  that config is via Render's [Secret Files](#using-render-secret-files-for-config)
  feature — see below.
</Tip>

## Reproducible deploys with `render.yaml`

If you'd rather declare the deployment in code (so you can recreate it from
scratch, version it, or share it with teammates), drop this `render.yaml` at
the root of any GitHub repo and link Render to it. Render watches the file
and applies changes automatically.

```yaml theme={}
# render.yaml — Infrastructure as Code for a Comis deployment
services:
  - type: web
    name: comis
    runtime: image
    image:
      url: docker.io/comisai/comis:<version>-slim   # replace with an immutable tag
    plan: starter                                  # $7/mo, has persistent disk
    region: oregon                                 # closest to your users
    healthCheckPath: /health
    autoDeploy: false                              # don't auto-redeploy on every image push

    envVars:
      # Required networking — let the container listen on all interfaces
      # so Render's load balancer can route to it
      - key: COMIS_GATEWAY_HOST
        value: 0.0.0.0
      - key: COMIS_GATEWAY_PORT
        value: 4766

      # Auto-generated — Render creates a strong random value at first deploy
      # and stores it; the same value is reused on subsequent deploys
      - key: COMIS_GATEWAY_TOKEN
        generateValue: true

      # User must set these in the Render dashboard after first deploy
      # (sync: false means "don't store this value in render.yaml")
      - key: ANTHROPIC_API_KEY
        sync: false
      - key: TELEGRAM_BOT_TOKEN
        sync: false
      - key: DISCORD_BOT_TOKEN
        sync: false
      - key: SLACK_BOT_TOKEN
        sync: false

    disk:
      name: comis-data
      mountPath: /home/comis/.comis
      sizeGB: 1
```

Render's docs on this file format: [render.com/docs/blueprint-spec](https://render.com/docs/blueprint-spec).

## Using Render Secret Files for config

If you have an existing `config.yaml` (e.g. from a local install) and want to
seed it on Render rather than configure through the dashboard:

<Steps>
  <Step title="Open Render dashboard for your service">
    Go to your service's page → **Environment** tab in the left sidebar.
  </Step>

  <Step title="Add a Secret File">
    Scroll to **Secret Files** → click **Add Secret File**. Enter:

    | Field             | Value                               |
    | ----------------- | ----------------------------------- |
    | **Filename**      | `/etc/comis/config.yaml`            |
    | **File Contents** | Paste your full local `config.yaml` |

    Click **Save**.
  </Step>

  <Step title="Patch the config for the container">
    Two changes are needed in the version you paste, compared to a typical
    local-install config:

    **1. Remove any `dataDir:` line at the top.** Local configs from the
    install wizard usually have `dataDir: /Users/yourname/.comis` (a host
    absolute path that doesn't exist inside the container). The image already
    sets `COMIS_DATA_DIR=/home/comis/.comis` and the persistent disk mounts
    there — drop the line and the daemon does the right thing.

    **2. Set `gateway.host: 0.0.0.0`** (a typical install has `127.0.0.1`,
    which means "loopback only" — Render's load balancer can't reach it).

    Example before / after:

    ```diff theme={}
    - dataDir: /Users/yourname/.comis
      logLevel: info
      agents:
        default:
          name: my-agent
          provider: anthropic
          model: default
      gateway:
        enabled: true
    -   host: 127.0.0.1
    +   host: 0.0.0.0
        port: 4766
    ```
  </Step>

  <Step title="Redeploy">
    Render auto-restarts the service when you save a Secret File. Watch the
    Logs tab — you should see your existing channels and agents register on
    boot.
  </Step>
</Steps>

## Updating to a new Comis version

If you pinned a version tag, update it deliberately after reviewing the release:

<Steps>
  <Step title="Find the latest version">
    Visit [hub.docker.com/r/comisai/comis/tags](https://hub.docker.com/r/comisai/comis/tags) or the
    [Comis releases page](https://github.com/comisai/comis/releases).
  </Step>

  <Step title="Update the image URL">
    On Render → your service → **Settings** → scroll to **Image URL** → change
    the existing immutable tag to the new version → **Save Changes**.

    (Or, if you used `render.yaml`, edit the file in your repo and push — Render
    picks up the change.)
  </Step>

  <Step title="Render redeploys automatically">
    The service may be unavailable while Render pulls the new image, runs the
    health check, and switches traffic over. Your
    persistent disk is reattached unchanged, so all memory and config carry over.
  </Step>
</Steps>

If you're on `latest-slim` instead, click **Manual Deploy** → **Deploy latest commit**.

## Custom domain

Render gives every service a `*.onrender.com` URL with HTTPS for free. To use
your own domain:

<Steps>
  <Step title="Add the domain in Render">
    Service page → **Settings** → **Custom Domains** → **Add Custom Domain** →
    enter your domain (e.g. `comis.example.com`). Render shows you a CNAME
    target.
  </Step>

  <Step title="Set the DNS record">
    At your DNS provider, create a CNAME record pointing
    `comis.example.com` → the Render-provided target. Wait for DNS to
    propagate.
  </Step>

  <Step title="Render provisions TLS">
    Render auto-issues a Let's Encrypt cert once the CNAME resolves. The
    custom-domains panel on Render shows the cert status.
  </Step>
</Steps>

No Comis-side configuration is needed for custom domains. Render terminates
TLS at the edge and forwards plain HTTP to your container on port 4766.

## Cost

| Plan           | Monthly | Persistent disk               | Sleeps when idle? | Comis-suitable                    |
| -------------- | ------- | ----------------------------- | ----------------- | --------------------------------- |
| Free           | \$0     | ❌                             | After 15 min      | ❌                                 |
| **Starter**    | **\$7** | ✅ (1 GB included; +\$0.25/GB) | Never             | ✅                                 |
| Standard       | \$25    | ✅                             | Never             | ✅                                 |
| Pro / Pro Plus | \$85+   | ✅                             | Never             | ✅ (only if you need more CPU/RAM) |

Most personal Comis deployments run comfortably on Starter — the daemon's
baseline memory is around 200–400 MB and CPU is low except during LLM calls.

The bulk of your spend is **LLM API costs**, not Render. A medium-traffic
single-user agent typically runs $0.50–$5/mo in Anthropic charges
(see [Context Engine & Cost Optimization](https://github.com/comisai/comis#context-engine--cost-optimization)
for cache savings) — much more than Render's \$7.

## Common issues

<AccordionGroup>
  <Accordion title="The service won't start (immediate restart loop)">
    Open the **Logs** tab and look near the start of the most recent boot.
    Most common causes:

    * **`FATAL: Bootstrap failed: Config validation failed: gateway.tokens.0.secret: Too small`**
      You provided a `COMIS_GATEWAY_TOKEN` shorter than 32 characters. Generate
      a fresh one with `openssl rand -hex 32` (which produces 64 hex chars,
      well over the 32-char minimum) and update the env var in Render.

    * **`FATAL: EACCES: permission denied, mkdir '/Users/...'`**
      Your `config.yaml` has a `dataDir:` line pointing at a host absolute path
      from a local machine. Remove that line — see
      [Patch the config for the container](#using-render-secret-files-for-config).

    * **Health check timing out**
      Render's default health check timeout is 30s, and Comis's first boot
      can take 10–25s while it initializes the SQLite databases on the
      newly-attached disk. If it consistently times out, bump the health-check
      timeout in service settings.
  </Accordion>

  <Accordion title="`/v1/chat/completions` returns 502 Bad Gateway">
    Render's load balancer returns 502 when it can't reach your container.
    Almost always: the gateway is bound to `127.0.0.1` (loopback inside the
    container) instead of `0.0.0.0` (all interfaces).

    **Fix:** Verify both of these are set:

    * Env var **`COMIS_GATEWAY_HOST=0.0.0.0`** (Settings → Environment)
    * Your `config.yaml` (if you uploaded one) has **`gateway.host: 0.0.0.0`** —
      not `127.0.0.1`. Config-file value wins over env var.

    After fixing, click **Manual Deploy → Deploy latest commit** to restart.
  </Accordion>

  <Accordion title="The agent loses memory after every redeploy">
    You forgot to add the persistent disk, or its mount path is wrong.

    **Fix:** Service → **Settings** → scroll to **Disks** → confirm:

    * There is a disk attached
    * **Mount Path** is exactly `/home/comis/.comis` (this is the path the daemon
      reads — anything else gets ignored)

    If you previously deployed without the disk and lost data, that's
    unrecoverable — but adding the disk now will preserve everything from
    this point forward.
  </Accordion>

  <Accordion title="Can the agent use the `exec` skill on Render?">
    **Yes.** Render runs containers on real Linux kernels, so the bubblewrap
    exec sandbox is fully active — you'll see
    `"Exec sandbox provider detected" provider=bwrap` at startup, with no
    `Exec sandbox DISABLED` warning.

    This is the major difference from Docker Desktop on macOS, where the
    linuxkit kernel forces the daemon into auto-disabled-sandbox mode. On
    Render, agent-issued shell commands run in a per-command sandbox that
    excludes `/home/comis/.comis` from its mount set — prompt-injected exec
    cannot read your `.env`, `secrets.db`, or config tokens.
  </Accordion>

  <Accordion title="Logs aren't showing anything I expect">
    Render's **Logs** tab shows everything the container writes to stdout,
    which is full Pino JSON. To make it readable in your browser, use Render's
    built-in filter box at the top of the tab — for example, type
    `"level":50` to see only ERROR-level lines.

    For richer log analysis (cost summaries, cache hit rates, request
    timings), download the logs and run them through `jq`:

    ```bash theme={}
    # Download via Render API or copy from the dashboard
    cat render-logs.txt | grep '^{' | jq -c 'select(.msg=="Execution complete")'
    ```

    The structured fields include `durationMs`, `tokensIn`, `tokensOut`,
    `cacheHitRate`, `costUsd` per LLM call.
  </Accordion>

  <Accordion title="My Telegram bot isn't responding">
    Common causes, in order of likelihood:

    1. **`TELEGRAM_BOT_TOKEN` env var not set** — the daemon logs
       `"Channel telegram disabled"` at startup if the token is missing or
       invalid. Add the var, redeploy.
    2. **`config.yaml` doesn't have `channels.telegram.enabled: true`** —
       even if the token is set, the channel must be enabled in config.
    3. **Telegram bot username collision / wrong token** — easy to mix up
       tokens between bots if you have several. Test by hitting
       `https://api.telegram.org/bot<YOUR_TOKEN>/getMe` — that endpoint returns
       the bot's identity, so you can confirm the token belongs to the bot
       you expect.
    4. **`allowFrom` filtering** — your config may have `channels.telegram.allowFrom`
       set to a Telegram user ID that doesn't include the sender. The
       daemon logs the rejected sender at DEBUG level — search the logs for
       `not in allowFrom`.
  </Accordion>

  <Accordion title="How do I `exec` into the running container for debugging?">
    Render → service page → **Shell** tab in the left sidebar. This opens an
    interactive shell inside the running container, running as the `comis`
    user. From there you can:

    ```bash theme={}
    # Inspect the daemon's data dir
    ls -la /home/comis/.comis

    # Tail logs from inside the container
    tail -f /home/comis/.comis/logs/daemon.1.log

    # Inspect memory.db
    sqlite3 /home/comis/.comis/memory.db .tables

    # Check what env vars the daemon sees
    cat /proc/1/environ | tr '\0' '\n' | grep -E '^(COMIS_|ANTHROPIC|TELEGRAM)'
    ```

    The shell is read-write on the persistent disk, so be careful — anything
    you change here lasts across deploys.
  </Accordion>

  <Accordion title="How do I rotate the gateway token?">
    1. Generate a new one: `openssl rand -hex 32`
    2. Render → service → **Environment** → update `COMIS_GATEWAY_TOKEN`
       with the new value
    3. Click **Save Changes** — Render auto-restarts the container
    4. Update any clients (the dashboard prompt, your scripts, etc.) to use
       the new token

    The old token stops working immediately on restart. There's no overlap
    window — for zero-downtime token rotation you'd need two tokens
    configured simultaneously (`gateway.tokens` is an array in `config.yaml`),
    use the new one in clients, then remove the old entry.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Configuration Guide" icon="gear" href="/installation/configuration">
    Add agents, set policies, configure prompt caching, define channels.
  </Card>

  <Card title="Verify Installation" icon="circle-check" href="/installation/verify">
    Diagnostic commands and expected outputs for a healthy install.
  </Card>

  <Card title="Operations: Docker" icon="docker" href="/operations/docker">
    The full Docker reference — multi-stage build, security model, sandbox
    behavior, secrets management.
  </Card>

  <Card title="Docker Hub Tags" icon="tag" href="/operations/docker-hub">
    Tag scheme, version pinning, image variants (default vs slim).
  </Card>
</CardGroup>
