Spaces:
Sleeping
Sleeping
| title: OpenCode Cloud | |
| emoji: π€ | |
| colorFrom: gray | |
| colorTo: indigo | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| license: mit | |
| # opencode-cloud | |
| Run [opencode](https://opencode.ai) β an open source AI coding agent β as a hosted | |
| server you reach from a browser, from the desktop app, or from a local build of the | |
| web app. One Dockerfile, deployable to **Hugging Face Spaces** or **Railway**. | |
| `opencode serve` is not just an API: the server also serves the web UI on its | |
| catch-all route, so opening the deployment URL gives you the full app, same-origin. | |
| --- | |
| ## Deploy on Hugging Face Spaces | |
| 1. **Create a Space** β SDK **Docker**, blank template. Push this repo to it | |
| (or duplicate an existing Space built from it). | |
| 2. **Settings β Variables and secrets** and add: | |
| | Secret | Required | Notes | | |
| |---|---|---| | |
| | `OPENCODE_SERVER_PASSWORD` | **yes** | The container refuses to start without it. This is the only thing protecting a shell. | | |
| | `OPENCODE_SERVER_USERNAME` | no | Defaults to `opencode`. | | |
| | `OPENCODE_API_KEY` | pick β₯1 | [opencode zen](https://opencode.ai/zen) curated models | | |
| | `ANTHROPIC_API_KEY` | pick β₯1 | https://console.anthropic.com | | |
| | `OPENAI_API_KEY` | pick β₯1 | https://platform.openai.com/api-keys | | |
| | `GEMINI_API_KEY` | pick β₯1 | https://aistudio.google.com/apikey | | |
| 3. Open the Space URL and log in with `opencode` + your password. | |
| Provider keys work as plain env vars β opencode registers any provider whose | |
| models.dev env var is present, no login step needed. | |
| ### Persistence | |
| Free Spaces are **ephemeral** and are stopped after ~48h idle. Sessions, provider | |
| logins and any uncommitted code are lost on restart. Two options: | |
| - **Free:** `git clone` your repo into the workspace and push before you walk away. | |
| - **Paid:** enable persistent storage (Settings β Storage). It mounts at `/data`, | |
| which `entrypoint.sh` detects and uses for the XDG dirs *and* the workspace, so | |
| state survives restarts. Watch the boot log β it prints which mode it picked. | |
| ### Public or private Space? | |
| A **private** Space adds your HF login in front of everything, which is the safer | |
| default for browser use. The catch: HF authenticates private Spaces with an | |
| `Authorization: Bearer hf_β¦` header, and opencode wants `Authorization: Basic β¦` β | |
| one header, two claimants. So a private Space works in a browser (HF session | |
| cookie) but a **desktop-app or local-app connection to a private Space will fight | |
| over that header**. Keep the Space public with a long password if you want those. | |
| --- | |
| ## Deploy on Railway | |
| ```bash | |
| railway init # or point a service at this GitHub repo | |
| railway variables --set OPENCODE_SERVER_PASSWORD=... | |
| railway up | |
| ``` | |
| Railway injects `$PORT`, which `entrypoint.sh` uses automatically. Attach a volume | |
| with mount path `/data` for persistence. Details and caveats: [docs/RAILWAY.md](docs/RAILWAY.md). | |
| --- | |
| ## Run as a GitHub Codespace | |
| ``` | |
| Code -> Codespaces -> Create codespace on main | |
| ``` | |
| Set `OPENCODE_SERVER_PASSWORD` first, as a **Codespaces** secret β repository secrets | |
| and Actions secrets are separate and are not visible here: | |
| ```bash | |
| gh secret set OPENCODE_SERVER_PASSWORD --app codespaces --repo OWNER/REPO | |
| ``` | |
| `.devcontainer/devcontainer.json` builds the same Dockerfile, forwards 7860 publicly and | |
| starts the server on `postStartCommand`. Public is safe only because `entrypoint.sh` refuses | |
| to start without a password; without the secret the codespace comes up with no server and the | |
| reason is in `/tmp/opencode.log`. | |
| Unlike a Space or a Railway service, a codespace **stops after 30 minutes idle** (240 maximum). | |
| It keeps its disk and restarts under the same name and URL, and `postStartCommand` boots the | |
| server again β but it is not always-on, and GitHub deletes a stopped codespace after the | |
| retention period, up to 30 days. State lives under `/workspaces/.opencode-state`, so it | |
| survives a stop and is lost only with the codespace itself. | |
| A personal Free account includes 120 core-hours a month, which is roughly 60 hours of wall | |
| clock on a 2-core machine; the idle timeout is what keeps that from draining. | |
| --- | |
| ## Keeping the Space in sync with GitHub | |
| `.github/workflows/sync-to-hf-space.yml` builds the image, boots it, checks that | |
| it refuses to start unauthenticated and that it serves an authenticated API and | |
| the web UI β and only then force-pushes `main` to the Space and waits for it to | |
| report `RUNNING`. A broken Dockerfile fails on the runner instead of leaving the | |
| Space stuck in `BUILD_ERROR`. | |
| Configure it under **Settings β Secrets and variables β Actions**: | |
| | Name | Kind | Notes | | |
| |---|---|---| | |
| | `HF_TOKEN` | **secret** | A Hugging Face **write** token. The HF username is derived from it, so it is the only secret needed. | | |
| | `HF_SPACE` | variable | `owner/space-name`. Optional β defaults to this GitHub repo's `owner/name`. A public Space id isn't sensitive, so a variable keeps it readable in logs; a secret of the same name also works. | | |
| GitHub is the source of truth: the sync **force-pushes**, so a commit made only | |
| in the Space's web UI will be discarded. Edit here, not there. | |
| --- | |
| ## Connecting the desktop app or a local web app | |
| You don't have to use the in-browser UI. In the app, **Settings β Servers β Add** | |
| and enter the deployment URL plus the username/password. The server's CORS | |
| allowlist already covers `localhost`, `*.opencode.ai` and the desktop app's | |
| `oc://renderer` origin, so no extra flags are needed. Only a frontend you host on | |
| your own domain needs `OPENCODE_CORS_ORIGINS=https://your.domain`. | |
| --- | |
| ## Giving the agent Hugging Face access | |
| Set `HF_TOKEN` as a Space secret (or Railway variable) and the agent can create | |
| and manage Hugging Face repos: the official `hf` CLI is installed in the image, | |
| it picks the token up from the environment with no login step, and `entrypoint.sh` | |
| installs [`agents/hf-spaces.md`](agents/hf-spaces.md) as opencode's global | |
| `AGENTS.md` so the agent knows the recipes. With no `HF_TOKEN` set, that file is | |
| removed and nothing advertises the capability. | |
| > **This hands your HF account to whoever can reach the server.** A standard HF | |
| > write token covers every repo the account can write to β including org repos β | |
| > so the blast radius is much larger than "a shell in a container". Two things | |
| > make it meaningfully safer: | |
| > | |
| > - Use a **fine-grained token** scoped to just the permissions you need (write | |
| > access to Spaces, say) rather than an account-wide write token. | |
| > - Remember the agent reads untrusted content β repos, web pages, issue text. | |
| > Anything it reads can try to talk it into using the token. The instructions | |
| > tell it never to print the token, but that is a speed bump, not a boundary. | |
| --- | |
| ## Read this before you deploy | |
| - **This is a remote shell.** Every route allows command execution and file | |
| read/write in the container. Basic auth over HTTPS is the entire security model, | |
| so use a long random password, and think twice about what credentials you put in | |
| the container alongside it. | |
| - **Provider OAuth "login with browser" flows don't work on a remote server.** | |
| They bind a `http://localhost:<port>/auth/callback` listener *inside* the | |
| container, so your browser is redirected to your own machine instead. Use API | |
| keys. | |
| - **CPU only, and modest.** Fine for the agent; you can't run local models. | |
| - **Check the host's terms.** A general-purpose remote shell is not the ML-demo | |
| use case Spaces are described for; a public one with a weak password is the way | |
| to get flagged. | |
| --- | |
| ## Environment variables | |
| | Variable | Default | Purpose | | |
| |---|---|---| | |
| | `OPENCODE_SERVER_PASSWORD` | β | **Required.** Basic auth password. | | |
| | `OPENCODE_SERVER_USERNAME` | `opencode` | Basic auth username. | | |
| | `PORT` | `7860` | Listen port. Railway sets this; HF must match `app_port`. | | |
| | `OPENCODE_STATE_ROOT` | `/data` | Where to look for a writable volume. Codespaces sets `/workspaces/.opencode-state`. | | |
| | `OPENCODE_WORKSPACE` | `$STATE_ROOT/workspace` or `$HOME/workspace` | Directory to serve. | | |
| | `OPENCODE_CORS_ORIGINS` | β | Comma-separated extra CORS origins. | | |
| | `HF_TOKEN` | β | Optional. A Hugging Face token, which lets the agent create and manage HF repos with the bundled `hf` CLI. See the warning below. | | |
| Build arg `OPENCODE_VERSION` pins a release (default: latest). | |
| --- | |
| MIT. opencode itself is Β© Anomaly Innovations; this repo is deployment wrapper code. | |