opencode-cloud / .devcontainer /devcontainer.json
polats's picture
Keep the server alive across a restart, and persist its state
a38523f
Raw
History Blame Contribute Delete
3.1 kB
Invalid JSON:Unexpected token '/', "// opencod"... is not valid JSON
// opencode-cloud as a GitHub Codespace β€” a third home alongside Hugging Face Spaces and
// Railway.
//
// The same Dockerfile serves all three. What differs here is that a dev container starts with
// its entrypoint overridden, so the image's ENTRYPOINT never runs: the server has to be
// started explicitly, and started again after every idle stop.
//
// Codespaces stop after 30 minutes idle by default. That is fine for this use β€” a stopped
// codespace keeps its disk and restarts under the same name and URL β€” but it does mean
// postStartCommand, not postCreateCommand, is what boots the server, because only the former
// runs on every resume.
{
"name": "opencode-cloud",
"build": {
"dockerfile": "../Dockerfile"
},
// The image ships no SSH server, and without one `gh codespace ssh` and `gh codespace logs`
// both refuse β€” so a codespace that fails to start the server cannot be inspected at all.
// This costs nothing at runtime and is the difference between a debuggable box and a black
// one.
"features": {
"ghcr.io/devcontainers/features/sshd:1": {}
},
// uid 1000, matching the image. The Dockerfile creates nothing else.
"remoteUser": "node",
// Hugging Face has no $PORT and falls back to 7860; keep the same here so one number
// describes the server everywhere.
"containerEnv": {
"PORT": "7860",
// entrypoint.sh persists sessions, logins and code under this root when it is writable.
// /data does not exist in a codespace, but /workspaces survives stop/start, so state
// outlives an idle timeout and is lost only when the codespace itself is deleted.
"OPENCODE_STATE_ROOT": "/workspaces/.opencode-state"
},
"forwardPorts": [7860],
"portsAttributes": {
"7860": {
"label": "opencode",
// The only way to expose a port without a human toggling it: there is no REST endpoint
// for port visibility, so a client that provisions codespaces has to declare it here.
//
// Public is safe *because* entrypoint.sh refuses to start without
// OPENCODE_SERVER_PASSWORD. Every route on this server runs shell commands, so basic
// auth is the only thing in front of it; a missing password fails loudly rather than
// publishing an open shell.
"visibility": "public",
"onAutoForward": "silent"
}
},
// Runs on create *and* on every resume from an idle stop, which is what makes the codespace
// behave like a server rather than a dev box you have to restart by hand. Verified across a
// stop/start cycle: the hook runs again and the server is serving within seconds.
//
// `bash -c` is not decoration. The hook is otherwise run by /bin/sh, where this line does
// not reliably detach and the server is lost the moment the hook returns β€” leaving a
// codespace that looks healthy and serves nothing.
"postStartCommand": "bash -c 'setsid nohup ${containerWorkspaceFolder}/.devcontainer/start-opencode.sh >> /tmp/opencode.log 2>&1 < /dev/null & sleep 2'",
"customizations": {
"vscode": {
"extensions": []
}
}
}