Skip to content
HostStack Docs

Move Your Dev Environment

How to get an existing project — repo, services, data, keys — onto a HostStack dev box. Everything below is a path that works today. Where a path doesn't exist yet, this page says so instead of inventing one; see What has no path yet.

New to dev boxes? Start with AI Dev Environments.

The short version. Code comes in over git. Backing services are already in the box — you don't move them, you start them. Data comes in over the network from somewhere the box can reach. Credentials go in through the dashboard or the terminal. There is no "upload my whole dev environment" button.

1. Bring the code

Git is the transport. Push whatever you have locally to a remote first, then point a box at it.

  • A GitHub repo (public or private) — pick it in the create form's Source step, or clone it from the terminal. No key needed either way: HTTPS clone and push authenticate through your team's HostStack GitHub App, and SSH-style GitHub remotes (git@github.com:owner/repo.git) are rewritten to HTTPS so they use it too.
  • Any other http(s) clone URL — paste it into Clone URL. The box clones it into /workspace/<repo> on first boot. The server rejects non-http(s) URLs at create time, so an ssh:// or git@ URL has to be cloned from the terminal instead (below).
  • A GitLab / Bitbucket / Codeberg / self-hosted repo — start a Blank box, install a deploy key, and clone over SSH. HostStack's credential helper only covers GitHub, so an HTTPS clone from those hosts will prompt for credentials it can't supply.

Installing a key and cloning, in the box's terminal:

bash
# ~/.ssh is a symlink to /workspace/.ssh, so this survives restarts.
cat > ~/.ssh/id_ed25519 <<'KEY'
-----BEGIN OPENSSH PRIVATE KEY-----
…paste your private key…
-----END OPENSSH PRIVATE KEY-----
KEY
chmod 600 ~/.ssh/id_ed25519

# Name it id_hetzner if a repo's own deploy script expects that name.
ssh-keyscan gitlab.com >> ~/.ssh/known_hosts
git clone git@gitlab.com:you/app.git /workspace/app

Prefer not to paste a key into a terminal? Generate one in the box instead (ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519) and add the public half as a deploy key on the host. Nothing leaves the box that way.

2. Bring the backing services

You don't move these — the box already has them, running natively rather than in containers. There is no Docker daemon in a box, so a docker-compose.yml full of postgres, redis and friends is replaced, not run.

bash
# Run it anyway — the shim reads YOUR compose file and maps it
docker compose up

# What it tells you to run instead:
dev-services up                 # postgres :5432 · redis :6379 · meilisearch :7700
dev-services up mysql mongodb   # MariaDB :3306 · MongoDB :27017 (opt-in)
dev-services status

Connection strings, all localhost-only with trust auth (no password):

Compose imageIn the boxURL
postgres, postgis, pgvectordev-services uppostgresql://dev@localhost:5432/dev
redis, valkeydev-services upredis://localhost:6379
meilisearchdev-services uphttp://localhost:7700 (no master key)
mysql, mariadbdev-services up mysqlmysql://dev@127.0.0.1:3306/<db>
mongo, mongodbdev-services up mongodbmongodb://127.0.0.1:27017

Your app's .env probably names a database and a role that don't exist yet (postgres://postgres:postgres@localhost:5432/myapp is the classic). Make them rather than editing the config:

bash
dev-services createdb myapp            # role + database, owner "dev"
dev-services createdb myapp postgres   # …or owned by "postgres"

Anything with no in-box equivalent — Kafka, Elasticsearch, a vendor sidecar — has to run as a HostStack service or point at a remote instance; the docker shim lists exactly which of your compose services fall into that bucket. Data written by these engines lives on /workspace, so it survives restarts.

3. Bring the data

The box can reach out; nothing can reach in. There is no SSH server in the image, so rsync/scp/sftp into a box do not work. Every import is a pull, run from the box's terminal.

  • Small files (≤ 16 MB each) — drag, paste, or use the Attach button in the Terminal. The file lands in /workspace/.uploads/ and its path is pasted at your cursor.
  • Anything larger — put it somewhere the box can fetch (object storage with a signed URL, a release asset, a server you own) and pull it with curl, wget, git, or outbound scp. openssh-client is installed, so scp you@your-server:/backups/db.dump /workspace/ works — from the box, toward a host that accepts SSH.
  • A live database you can reach — skip the file entirely and stream it.
bash
# Postgres → the in-box engine (psql/pg_dump are installed)
dev-services up
dev-services createdb myapp
pg_dump "postgresql://user:pw@old-host:5432/myapp" | psql -d myapp

# …or from a dump file you pulled in
pg_restore -d myapp /workspace/myapp.dump

# MySQL/MariaDB
dev-services up mysql
mariadb -u dev -e 'CREATE DATABASE IF NOT EXISTS myapp'
mariadb-dump -h old-host -u user -p myapp | mariadb -u dev myapp

# Redis: usually not worth moving — let it rebuild.
MongoDB has no dump tooling. The image ships mongod and mongosh, but not mongodump/mongorestore. Move Mongo data as JSON through mongosh, or restore into a managed MongoDB and point the box at it.

If the data is the point — a staging dataset you want backed up and restorable — put it in a managed database instead of the box, and link that database to the box so its URL is injected. Managed databases are dumped daily and restorable from the dashboard; /workspace is not.

bash
hoststack db create --project prj_… --name app-db --engine postgres
hoststack db link db_… --service <dev-box-id> --alias APP
hoststack deploy trigger <dev-box-id>    # link applies on the next deploy

# then, in the box: DATABASE_URL is already set
psql "$DATABASE_URL" -c 'select 1'

4. Bring your credentials

  • Coding-agent logins — save one per agent under Settings → Coding Agents. It is stored encrypted on your account and seeded into every box on boot, so you never do this per box. Already logged in inside a box? Development → Setup → Capture from this box.
  • API keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, POSTSTACK_API_KEY, anything your app needs) — set them as variables under Settings → Variables on the box. The HostStack MCP key is provisioned for you; you don't need to set it.
  • SSH keys — no dashboard form; write them into /workspace/.ssh/ from the terminal as shown above.
  • Git identity Development → Setup → Git identity, or your account default under Settings → Coding Agents. Commits fail with "please tell me who you are" rather than being misattributed if neither is set.

5. Bring your shell setup

Only /workspace persists. The root filesystem — including $HOME and ~/.bashrc — is rebuilt from the image every time the container is recreated (a redeploy, or power down → resume). So put dotfiles on the volume and source them:

bash
git clone https://github.com/you/dotfiles /workspace/.dotfiles

# For this shell, and every new one this boot:
echo '. /workspace/.dotfiles/bashrc' >> ~/.bashrc
. /workspace/.dotfiles/bashrc
Nothing seeds dotfiles for you. The ~/.bashrc line above is on the ephemeral root layer, so you re-add it after a recreate. Only the things HostStack owns — credentials, git config, agent logins, caches, mise runtimes, TMPDIR — are re-seeded onto the volume automatically.

Globally-installed CLI tools persist too, because the image points them at the volume: bun add -g installs under /workspace/.bun and pip install --user under /workspace/.local, both on PATH. A language toolchain added with dev-runtime add lands in /workspace/.mise, and a project-local node_modules/vendor is on the volume by virtue of being in your repo. apt-get install is the one that still cannot work — system packages go to the image layer, so anything you need from apt belongs in a request to us rather than in a box. Keeping a one-line setup script in your repo is still the reliable answer for a box you might recreate.

6. First-run checklist

bash
hoststack-status                  # what's up, the dev URL, image build
dev-services up                   # + mysql / mongodb if your stack needs them
dev-services createdb myapp       # match the database name your .env expects
dev-runtime add go                # only if your runtime isn't node/bun/python/php
cd /workspace/app && bun install  # or npm ci / composer install / …
PORT=3000 HOST=0.0.0.0 bun run dev

Then open the box's dev URL from the workspace header. If you get the branded "No app is listening on port 3000 yet" page (HTTP 503), nothing is bound to port 3000; if it's rejected as an invalid host, add .hoststack.dev to your framework's allowed-hosts list — see Serve on port 3000.

What has no path yet

These are real gaps, listed so you can plan around them rather than hunt for a feature that isn't there:

  • No workspace import, and no /workspace restore. You cannot upload a tarball of an existing environment, and there is no snapshot of a box's volume to restore from. Move code through git and data through a database.
  • No inbound SSH, so no rsync/scp into a box. Every transfer is initiated from inside the box.
  • The 16 MB terminal upload is per file, one at a time. It is for screenshots and logs, not archives.
  • No dotfile seeding. See above.
  • GitLab, Bitbucket and Codeberg aren't source options in the create form, even though HostStack integrates with them for deployed services. Use a blank box plus an SSH clone.
  • You can't attach an existing managed database at create time — the companion toggles only stand up fresh ones. Link an existing database afterwards with hoststack db link.
  • The CLI can't open a box: there is no dev shell, dev exec or dev open. Use the dashboard Terminal.
  • No hoststack teams command. On more than one team, set HOSTSTACK_TEAM_ID in your environment to choose which one CLI commands act on.
  • No mongodump/mongorestore in the image.

Essential cookies only — for login sessions. No tracking. Details