> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Host OpenCode on Upsun

> Deploy an OpenCode AI coding agent on Upsun with a single git push. This tutorial covers configuration, persistent storage, LLM API keys, and remote MCP server integration.

export const PostMeta = ({data = {}}) => {
  const {author, date} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud",
      "linkedin": "https://www.linkedin.com/in/laurent-arnoud-861b44121/"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "maz-mohammadi": {
      "name": "Maz Mohammadi"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
    </div>;
};

<PostMeta data={{ author: ["flovntp"], date: "2026-05-19T10:08:00.000Z", image: "/images/posts/ai/opencode-on-upsun/opencode.png" }} />

> [**OpenCode**](https://opencode.ai/) is an open-source AI coding agent built for the terminal. This guide walks you through deploying an
> OpenCode server instance on [Upsun](https://upsun.com), a git-driven PaaS (powered by Platform.sh) that makes it easy
> to deploy applications with a single `git push`.

## Prerequisites

Before starting, make sure you have:

* An [Upsun account](https://auth.upsun.com/register) (15-day free trial available)
* The [Upsun CLI](/cli/install#install-the-upsun-cli) installed
* [Git](https://git-scm.com/) installed
* An API key for at least one LLM provider (e.g., Anthropic, OpenAI, Google Gemini)

## Overview

[OpenCode](https://opencode.ai/) is an application with a client/server architecture.
In this tutorial, you deploy the **OpenCode server** on Upsun and connect to it remotely.
The server exposes an HTTP API, which allows you to drive OpenCode from any client (TUI, IDE extension, or web) without
having the agent running locally.

The deployment relies on a `nodejs:24` runtime, persistent storage for the SQLite database that stores conversation
sessions, environment variables to securely pass LLM API keys, and routes to expose the server over HTTPS.

## Step 1 - Set up your project

Clone or initialize a repository that contains your OpenCode configuration. If you are starting from scratch, create a new directory:

```bash title="Terminal" theme={null}
mkdir opencode-upsun
cd opencode-upsun
git init
```

## Step 2 - Create the Upsun configuration

At the root of your project, create the `.upsun/` directory and add a `config.yaml` file:

```bash title="Terminal" theme={null}
mkdir .upsun
touch .upsun/config.yaml
```

Here is the full configuration:

<Tabs>
  <Tab title="Using NodeJS dependencies">
    ```yaml .upsun/config.yaml theme={null}
    applications:
      opencode:
        type: "nodejs:24"
        container_profile: "HIGH_MEMORY"
        source:
          root: /
        # Persistent mounts for OpenCode's SQLite database and config
        mounts:
          "/.local": {source: storage, source_path: "opencode-data"}
          "/.config/opencode": {source: storage, source_path: "opencode-config"}
          "/.agents": {source: storage, source_path: "opencode-agents"}
          "/.opencode": {source: storage, source_path: "opencode-source"}
        dependencies:
          nodejs:
            opencode-ai: "*" # OpenCode
            upsun: "*"       # Upsun CLI
        hooks:
          build: |
            set -ex

            echo "Install Upsun Skills..."
            npx skills add https://github.com/upsun/ai --skill upsun -g -y
            echo "Upsun Skills installed at $PLATFORM_APP_DIR/node_modules/.bin/upsun-mcp"
          deploy: |
            # Generate OpenCode config with the correct MCP server URL and API token
            envsubst < /app/opencode.json > /app/.config/opencode/opencode.json

        # Environment variables — API keys are injected as Upsun secrets
        variables:
          env:
            OPENCODE_THEME: "opencode"
            HOME: "/app"

        # Web configuration: route all traffic to the OpenCode HTTP server
        web:
          commands:
            start: "opencode serve --port $PORT"

    routes:
      "https://opencode.{default}/":
        type: upstream
        upstream: "opencode:http"
    ```
  </Tab>

  <Tab title="Using install-github-asset.sh script">
    ```yaml .upsun/config.yaml theme={null}
    applications:
      opencode:
        type: "nodejs:24"
        container_profile: "HIGH_MEMORY"
        source:
          root: /
        # Persistent mounts for OpenCode's SQLite database and config
        mounts:
          "/.local": {source: storage, source_path: "opencode-data"}
          "/.config/opencode": {source: storage, source_path: "opencode-config"}
          "/.agents": {source: storage, source_path: "opencode-agents"}
          "/.opencode": {source: storage, source_path: "opencode-source"}
        hooks:
          build: |
            set -ex
            
            echo "Downloading OpenCode..."
            curl -fsS https://raw.githubusercontent.com/upsun/snippets/main/src/install-github-asset.sh | bash /dev/stdin "anomalyco/opencode" "" "opencode-linux-x64.tar.gz"
            echo "OpenCode installed at $PLATFORM_APP_DIR/bin/opencode"

            echo "Install Upsun Skills..."
            npx skills add https://github.com/upsun/ai --skill upsun -g -y
            echo "Upsun Skills installed at $PLATFORM_APP_DIR/node_modules/.bin/upsun-mcp"

            echo "Install Upsun CLI..."
            curl -fsSL https://raw.githubusercontent.com/upsun/cli/main/installer.sh | bash
            echo "Upsun CLI installed at $(which upsun)"
          deploy: |
            # Generate OpenCode config with the correct MCP server URL and API token
            envsubst < /app/opencode.json > /app/.config/opencode/opencode.json

        # Environment variables — API keys are injected as Upsun secrets
        variables:
          env:
            OPENCODE_THEME: "opencode"
            HOME: "/app"

        # Web configuration: route all traffic to the OpenCode HTTP server
        web:
          commands:
            start: "opencode serve --port $PORT"

    routes:
      "https://opencode.{default}/":
        type: upstream
        upstream: "opencode:http"
    ```
  </Tab>
</Tabs>

Because Upsun's filesystem is read-only after the build step, the [`mounts`](/docs/configure-apps/image-properties/mounts#mounts) section defines writable directories so OpenCode can persist its SQLite database (sessions, conversations) and its config file across deploys.

The [`hooks.build`](/docs/configure-apps/hooks/index#use-build-and-deploy-hooks) section downloads and installs the OpenCode binary before the container starts, installs the Upsun skills (which include the MCP server), and the Upsun CLI.

The [`web.commands.start`](/docs/configure-apps/image-properties/web#start-command) section launches OpenCode in server mode. Upsun injects `$PORT` automatically, and your app must listen on that port.

Finally, [`routes`](/docs/routes) maps your project's default domain to the OpenCode HTTP server.

<Info>
  <h4>`install-github-asset.sh` script</h4>
  If you want to learn more about the script used to download OpenCode, check out the [Up(sun) and ready with GitHub release assets installation](/posts/hands-on/install-gh-asset-on-upsun/).
  It's a reusable script that can download any asset from any GitHub release, and is especially useful in Upsun build
  hooks where you want to fetch the latest version of a tool only once and, without hardcoding a specific version number.
</Info>

## Step 3 - Create the OpenCode configuration

OpenCode uses an `opencode.json` file to configure the server application.
This file configures the Upsun MCP server as a remote MCP server for OpenCode,
which allows OpenCode to execute commands on the Upsun platform and interact with its resources.

Create an `opencode.json` file at the root of your repository with the following content:

```json title="opencode.json" theme={null}
{
  "mcp": {
    "upsun": {
      "type": "remote",
      "url": "https://mcp.upsun.com/mcp",
      "headers": {
        "upsun-api-token": "${UPSUN_CLI_TOKEN}",
        "enable-write": "true"
      },
      "enabled": true
    }
  }
}
```

<Warning>
  The `"enable-write": "true"` header grants the OpenCode agent write access to your Upsun resources
  (creating environments, pushing code, changing variables, etc.). Remove this header or set it to `"false"`
  if you want read-only access.
</Warning>

## Step 4 - Commit your code

Commit your configuration:

```bash title="Terminal" theme={null}
git add .upsun/config.yaml opencode.json
git commit -m "Add Upsun and OpenCode configuration"
```

## Step 5 - Create the Upsun project

[Create a new Upsun project](/docs/get-started/here/create-project) linked to your local repository:

```bash title="Terminal" theme={null}
upsun project:create --title "opencode-server"
```

## Step 6 - Add your API keys as environment variables

Never commit API keys to your repository.
Use Upsun's [variable system](/docs/development/variables/index) instead.

```bash theme={null}
# Set your OpenAI API key as a sensitive variable
upsun variable:create --name env:OPENAI_API_KEY \
  --value sk-... \
  --sensitive true \
  --level project

# Or set your Claude API key as a sensitive variable
upsun variable:create --name env:ANTHROPIC_API_KEY \
  --value sk-... \
  --sensitive true \
  --level project

# Set the OpenCode server password
upsun variable:create --name env:OPENCODE_SERVER_PASSWORD \
  --value "<SECURE_PASSWORD>" \
  --sensitive true \
  --level project

# Set the Upsun API token (optional, for Upsun skill integration)
upsun variable:create --name env:UPSUN_CLI_TOKEN \
  --value "<UPSUN_API_TOKEN>" \
  --sensitive true \
  --level project
```

Sensitive variables are encrypted at rest and injected into the container at runtime. They will not appear in build logs.

You can also set them via the Upsun Console under **Settings → Variables**.

<Info>
  <h4>`OPENCODE_SERVER_PASSWORD` note</h4>
  By default, OpenCode's server mode requires a password for authentication.
  You can set this password via the `OPENCODE_SERVER_PASSWORD` environment variable.
  If you prefer to disable authentication (not recommended for production), you can set `OPENCODE_SERVER_PASSWORD` to an empty string.
</Info>

## Step 7 - Deploy

Push your code to Upsun:

```bash theme={null}
upsun push
```

Upsun will:

1. Validate your `.upsun/config.yaml`
2. Spin up a Node.js 24 container
3. Run the build hook:
   * downloading the OpenCode binary
   * installing the Upsun skills and MCP server
   * installing the Upsun CLI
4. Run the deploy hook, which generates the OpenCode config file with the correct MCP server URL and API token
5. Mount writable storage for the database and config
6. Start the OpenCode server on the assigned port
7. Route HTTPS traffic to it

Once complete, Upsun will display your app URL, something like:

```
https://main-abc123-yourproject.eu-5.platformsh.site
```

<Info>
  <h4>Login</h4>
  Open the URL in your browser. If you set `OPENCODE_SERVER_PASSWORD`, log in with the username `opencode` and
  the password you configured. You can also connect from the OpenCode TUI client by pointing it to the same URL.
</Info>

## Step 8 - Ask OpenCode to do something

OpenCode is now able to receive your commands from the TUI, execute them on the server, and return results.

Examples:

List existing organizations:

```
List organizations in the Upsun project
```

<Accordion title="Advanced: deploy a Symfony Demo app in a preview environment">
  Create a new Symfony application in a `symfony` subfolder and test it in a preview environment:

  ```
  You are in an existing Git repository linked to an existing Upsun project via PLATFORM_PROJECT.
  Use the Upsun skill, Upsun MCP, the Upsun CLI if available, and Git.

  Goal:
  On the current Upsun Project, add a new Symfony Demo application
  (use `symfony new app --demo --upsun` to generate it) in a new active
  environment named add-symfony-demo.
  - Symfony demo app `app` will be in a subfolder `symfony` at the root of
    the source code and the new app will point to that folder via source.root,
    will be accessible through the default route {default} and, must use
    Postgresql and Redis services.
  - Preserve existing source code and configuration, and add the existing
    Symfony demo configuration (--upsun option) to the main .upsun/config.yaml
  - Do not delete opencode.
  - Work only on the new environment.
  - Load the database schema and fixtures during first deploy.
  - You can use /tmp folder to install what you need.
  - Use the Upsun CLI to interact with the platform, do not use the API directly.
  ```
</Accordion>

You can test the new config without affecting your production instance. Merge the branch when satisfied, and Upsun
redeploys production.

## Troubleshooting

### The server does not start / PORT is not set

Upsun automatically injects `$PORT` into the environment of the `start` command. Make sure your start command references `$PORT` and that OpenCode's `serve` command accepts a `--port` flag. Check `opencode serve --help` for the exact flag name, as it may vary across versions.

### Sessions are not persisting across deploys

Verify that your mounts are defined correctly in `.upsun/config.yaml` and that the paths match where OpenCode writes its SQLite database. The database path can be checked with:

```bash theme={null}
upsun ssh -- opencode db:path
```

### API key not recognized

Confirm the variable is set in the correct environment:

```bash theme={null}
upsun variable:list
```

Sensitive variables show as `[hidden]` — that is expected. Make sure the variable name matches exactly what OpenCode expects (e.g., `ANTHROPIC_API_KEY`).

### OpenCode is slow to respond

If OpenCode is slow to respond, or stops in the middle of processing, check the resource usage of your Upsun container.
You may need to scale up CPU/RAM if your agent is handling large contexts or multiple conversations.

## What's next?

For the second episode in this series, see [How to slash OpenCode Token costs by 90% on Upsun](/posts/ai/opencode-token-optimization/) to reduce token usage on the deployment you just created.

You can attach a custom domain with `upsun domain:add yourdomain.com` and adjust CPU/RAM
through `upsun resources:set` if the agent handles large contexts or multiple conversations.

To extend OpenCode with additional [MCP server integrations](https://opencode.ai/docs/mcp-servers/),
configure them in `~/.config/opencode/` on the persistent mount. You can also use OpenCode's
[GitHub Actions integration](https://opencode.ai/docs/github/) to trigger the remote server on PR comments.

## Resources

* [OpenCode documentation](https://opencode.ai/docs/)
* [Upsun documentation](/docs)
* [Upsun Node.js runtime reference](/docs/languages/nodejs)
* [Upsun config.yaml reference](/docs/core-concepts/yaml/yaml-structure#upsun-yaml-structure)
* [OpenCode GitHub repository](https://github.com/anomalyco/opencode)
