AI Dev Environments
A dev environment on HostStack is a private service running our hoststack/dev-env image — a persistent cloud container with coding agents and MCP servers preinstalled. Open the Terminal tab (it works great from a phone) and run your agent on your repos, on European infrastructure.
git push from inside ships through your normal deploy pipeline.create_dev_environment (MCP), the AI Dev Environment wizard, or hoststack dev create — not with create_environment(type:"development"), which makes a project deploy target (production/staging) and never gives you a terminal. See the MCP reference for the tool surface.What's inside
- Coding agents:
claude(Anthropic Claude Code),codex(OpenAI Codex), andopencode(SST OpenCode) — all onPATH. - MCP servers wired into all three agents:
hoststack(manage your projects, services, deploys, databases, domains),poststack(email),filesystem, andgit. - Toolchain: git, GitHub CLI (
gh), Node 22, Bun, Python 3, uv, ripgrep, fzf, jq, tmux, postgresql-client, redis-tools, and the usual editors. - Persistent
/workspacevolume — your repos, agent logins andnode_modulessurvive restarts and redeploys.
Create one (one click)
In the dashboard, create a new service and pick the AI Dev Environment template. It pre-selects the image, makes the service private (no public ingress), attaches a 10 GB /workspace volume, and prompts you for the optional MCP API keys — then deploys.
The Development workspace
Every dev environment shows up in the dashboard's Development section — one place to switch between environments, open multiple terminal tabs per box (run an agent in one, git in another), and watch a live readout of what's running inside (the coding agent, your shells, and other processes). It works great from a phone.
Your dev URL (serve on $PORT)
Every box has a private, unguessable public URL that proxies to port 3000 inside the box. Start a dev server on 3000 bound to 0.0.0.0 and it's live at that URL — until then the URL shows a branded "no app listening" page. The box presets HOST=0.0.0.0, injects PORT=3000, and exports the URL as $HOSTSTACK_DEV_HOST.
The proxied port is hard-wired to 3000 — bind your dev server there. Setting a different PORT (in a dashboard env var or a committed .env) does not re-point the URL; it just moves your server off the port the proxy reaches, so the box looks empty. Always listen on 3000 (the injected $PORT) for the dev URL to work.
Dev servers (Vite, Next, Astro, …) reject requests whose Host header isn't allow-listed, so a plain run is refused at the dev URL even when bound to 0.0.0.0. Add the dev host to the framework's allowlist:
// Vite — vite.config.ts
export default defineConfig({
server: { host: true, allowedHosts: ['.hoststack.dev'] },
})
// Next.js — next.config.js
// Use the literal wildcard: HOSTSTACK_DEV_HOST is undefined unless you've
// exported it, and [undefined] allow-lists nothing.
module.exports = { allowedDevOrigins: ['*.hoststack.dev'] }Injected environment
HostStack sets a handful of variables in the container at boot. These are real environment variables, so under Bun/Node they take precedence over the same keys in a committed .env file (a committed PORT or DATABASE_URL is silently ignored). What's injected:
PORT(3000) andHOSTSTACK_DEV_HOST— the listen port and public dev host.HOSTSTACK_*— service/project/team IDs, internal URL, deploy ID, branch,HOSTSTACK_TMP_DIR.- Companion database URLs/creds when you attach them —
DATABASE_URL,POSTGRES_*,REDIS_*,MEILI_*. - Agent/MCP keys you save in Setup —
ANTHROPIC_API_KEY,OPENAI_API_KEY,HOSTSTACK_API_KEY,POSTSTACK_API_KEY.
Run printenv in the box to see the live set. To point at your own local stack instead, run dev-services up and export the URL in your shell (a shell export overrides the committed .env).
Log your agent in
Agent logins are account-wide: the default you pick per agent in Settings → Coding Agents is seeded into every box automatically, so you sign in once — not per box. For all three agents, log in inside a box (claude /login, codex login, opencode auth login) and use Development → Setup → Capture from this box to save it to your account — a real subscription login, so the full model menu (incl. Fable) works. For Claude you can also paste your ~/.claude/.credentials.json in Settings → Coding Agents. Logins persist on the /workspace volume:
# Claude Code — opens a device-code login flow
claude /login
# Then just run an agent
claude # interactive
codex # OpenAI Codex
opencode # SST OpenCodeSet up from the dashboard (Setup)
Open Development → Setup on a running box to provision it without touching the terminal. Two kinds of things you can save, with different storage:
- Box-only (never stored). The write-through Credentials panel — SSH keys, raw API keys pasted as env vars, and your git identity — is written straight into the box's
/workspacevolume and nowhere else. It persists across restarts on that box, but HostStack keeps no copy. - Account-wide (stored encrypted). Coding-agent logins (Claude / Codex / OpenCode) and the Google Search Console OAuth client are saved once under Settings → Coding Agents and seeded into every box. These are persisted server-side — encrypted at rest with AES-256-GCM — so you sign in once instead of per box.
What you can set here (each lands on the box's /workspace volume, so it persists across restarts and applies in new terminal sessions — the account-wide rows are also kept encrypted on your account, per above):
- SSH keys — upload a private key (name it
id_hetznerforscripts/deploy.sh, or any name for git-over-SSH). - Connect agents & MCP — paste
ANTHROPIC_API_KEY(Claude),OPENAI_API_KEY(Codex), andHOSTSTACK_API_KEY/POSTSTACK_API_KEY(MCP servers). They reach the agents with no redeploy. - Git identity — the name & email stamped on your commits.
- Search Console — save your Google OAuth client once under Settings → Coding Agents and it's seeded into every box (at
/workspace/.config/gsc-oauth-client.json, with theGSC_OAUTH_*env vars set) sobun run gsc:reportworks with no per-box upload.
Prefer the terminal? Subscription users can still run claude /login, and any of the above can be set with git config / export by hand. The filesystem and git MCPs work with no key.
Develop any repo — incl. hoststack/poststack
A dev box isn't limited to apps hosted on HostStack. To work on any git repo — including ones that deploy over SSH rather than a HostStack git push — start a blank box, then:
# 1. Development → Credentials → upload your SSH key as "id_hetzner"
# (and your git identity + agent keys)
# 2. Clone over SSH (persists on /workspace):
git clone git@github.com:you/hoststack.git
cd hoststack
# 3. Work with your agent, then ship however that repo ships —
# e.g. its own deploy script over SSH:
./scripts/deploy.shYour ~/.ssh and git config live on /workspace, so the key and identity survive every restart. You can also pre-clone at boot with hoststack dev create --repo <git-url>.
Code from your phone
The Terminal is built mobile-first. Install HostStack as a PWA (Add to Home Screen) or just open it in any mobile browser. Tap the terminal to bring up your keyboard; a soft-key bar gives you the keys phones lack — Esc, Tab, common Ctrl combos (Ctrl-C/D/Z/R/L), arrow keys, and shell symbols. The view tracks the on-screen keyboard so your prompt is never hidden. Kick off an agent, lock your phone, come back later — the session keeps running on our infra.
Shipping from inside
Your dev environment shares the same git + deploy plumbing as the rest of HostStack. Have your agent commit and git push, and the push hits your service's normal deploy webhook — no extra wiring. Clone any repo into /workspace and work on it there.
Billing
A dev environment is billed exactly like the private service it is — by the plan size you pick, plus the /workspace volume's per-GB-month storage. Power it down from the dashboard when you're not using it: the container is stopped so it no longer consumes compute. Anything running in it — terminal sessions, tmux, in-flight agents — is ended, but your /workspace (repos, agent logins, node_modules) is a persistent volume and is retained. Resume re-runs the box on its existing image in seconds.