Skip to content

Data Volumes

Sia edited this page May 31, 2026 · 5 revisions

Data Volumes & Backup

Every persistent path lives under one host directory (./vibe-coder-data/), making backup/migration a one-line tar. A postgres/ subdirectory holds the PostgreSQL data files (the database runs as a sidecar container).

Full mapping

./vibe-coder-data/                                       container
─────────────────                                        ─────────
├── workspace/                              →  /workspace                                       (sources + APKs)
├── postgres/                               →  vibe-coder-postgres : /var/lib/postgresql/data   (PG data dir)
├── server/                                 →  /data                                            (logs + build metadata)
├── dev-tools/
│   ├── android-sdk/                        →  /opt/android-sdk                                 (3-4 GB)
│   ├── gradle/                             →  /home/vibe/.gradle                               (1-2 GB)
│   ├── npm-global/                         →  /home/vibe/.local                                (MCP `npm -g`)
│   ├── npm-cache/                          →  /home/vibe/.npm                                  (npx cache)
│   ├── playwright/                         →  /home/vibe/.cache/ms-playwright                  (optional, ~300 MB)
│   ├── config/                             →  /home/vibe/.config                               (tool config)
│   ├── ssh/                                →  /home/vibe/.ssh                                  (SSH key for git clone/push)
│   └── keystores/                          →  /home/vibe/keystores                             (Android signing keys ⚠️ back up!)
└── claude/                                 →  /home/vibe/.claude                               (OAuth/API key/MCP registrations)

The server image itself (~600 MB) contains only the Ktor body + Claude CLI + JDK + Node — every byte that took effort to download lives in your host directory. vibe-coder-data/postgres/ holds the database, which runs as a sidecar container.

Ownership note (PostgreSQL bind mount)

vibe-coder-data/postgres/ is owned by the postgres user inside the container (UID 70 in postgres:17-alpine). On the host the directory will show as owned by UID 70 — that's expected and what PostgreSQL requires for secure file permissions. You will need sudo on the host to read/copy those files directly. tar as root or logical pg_dump (see Backup below) bypass that.

Backup

Three options, from simplest to most surgical:

Option 1 — Full snapshot (recommended for occasional backups)

# Stop both containers so PG and server are quiescent.
docker compose stop

# One-line tar (sudo because of the postgres dir ownership)
sudo tar czf vibe-coder-data-$(date +%F).tar.gz vibe-coder-data/
sudo chown $(id -u):$(id -g) vibe-coder-data-$(date +%F).tar.gz

# Resume
docker compose start

Option 2 — Logical PG dump while running

docker exec vibe-coder-postgres pg_dump -U vibecoder -F c vibecoder > vibe-pg-$(date +%F).pgdump
# To restore on another machine after `docker compose up -d`:
docker exec -i vibe-coder-postgres pg_restore -U vibecoder -d vibecoder --clean --if-exists < vibe-pg-*.pgdump

Logical dump skips workspace files / Android SDK / MCP — combine with tar czf workspace-$(date +%F).tar.gz vibe-coder-data/workspace/ to cover the rest.

Option 3 — Cron + retention

# /etc/cron.daily/vibe-coder-backup
#!/bin/sh
BAK=/srv/backup/vibe-coder
mkdir -p $BAK
docker exec vibe-coder-postgres pg_dump -U vibecoder -F c vibecoder > $BAK/pg-$(date +%F).pgdump
find $BAK -name 'pg-*.pgdump' -mtime +14 -delete

Restore / migrate to another machine

# On the destination
mkdir -p ~/vibe-coder && cd ~/vibe-coder
curl -fsSL https://raw.githubusercontent.com/siamakerlab/vibe-coder-server/main/docker/compose.yml -o compose.yml
curl -fsSL https://raw.githubusercontent.com/siamakerlab/vibe-coder-server/main/docker/.env.example -o .env

# REQUIRED: set VIBECODER_DB_PASSWORD to the same value you used on the source
# (otherwise the restored postgres data dir won't authenticate).
${EDITOR:-nano} .env

# Drop the archive into place
scp source-host:~/vibe-coder/vibe-coder-data-*.tar.gz .
sudo tar xzf vibe-coder-data-*.tar.gz   # sudo to preserve UID 70 inside postgres/

docker compose up -d

PUID / PGID change is the gotcha for everything except the PostgreSQL data dir. If id -u/-g on the destination differ from the source, the entrypoint chowns top-level dirs but not deep contents. If you see permission errors:

sudo chown -R $(id -u):$(id -g) vibe-coder-data/workspace vibe-coder-data/dev-tools vibe-coder-data/server vibe-coder-data/claude
# Leave vibe-coder-data/postgres/ alone — postgres needs UID 70 inside.

Or update .env PUID/PGID to match the source host's values.

Sizing guidance

Use case Recommended free disk
First boot, no projects yet ~5 GB (SDK 3-4 GB + image 600 MB + PG init ~50 MB + buffer)
Active project, ~10 builds done ~10 GB
Heavy use with Playwright + several MCP-installed packages ~15 GB
Production-leaning (artifact retention 20+ APKs each ~100 MB + long history) ~20-30 GB

The artifactKeepCount in server.yml (default 20) bounds APK growth. PostgreSQL initial size is ~50 MB and grows mostly with the conversation_turns table.

What's NOT in ./vibe-coder-data/

  • Docker container's own writable layer — ephemeral, recreated on --force-recreate. If you put state here by accident (writing to a non-mounted path inside the container), it'll vanish.

  • Image layers — pulled fresh on docker compose pull.

  • Host SSH agent — not forwarded. The container has its own SSH key at /home/vibe/.ssh/id_ed25519, persisted in ./vibe-coder-data/dev-tools/ssh/. The entrypoint auto-generates the keypair on first boot and never overwrites it on subsequent boots, so the key survives image upgrades. View / copy / regenerate via Settings → SSH Key in the admin UI. See SSH-Key for details.

  • Android keystores (./vibe-coder-data/dev-tools/keystores/) ⚠️. Managed via Settings → Keystores — each Android package name maps to a 4-file set: <pkg>.keystore (release), <pkg>-debug.keystore (debug), <pkg>-keystore.properties (Gradle signing config), and optionally <pkg>-admob.properties (AdMob IDs). Losing the release key blocks Play Store updates for that package forever — back up this directory after every new keystore creation. The defaults form is pre-filled from server.yml's keystore.defaults section (CN / O / OU / country / city

    • default password + validity years), so once the operator fills those out, every subsequent keystore only needs the package name. Builds load the properties file via standard Gradle Properties().load(FileInputStream( "/home/vibe/keystores/<pkg>-keystore.properties")) — see your Android project's app/build.gradle.kts.

docker compose down -v warning

docker compose down -v removes Docker named volumes. The bundled compose uses bind mounts only (everything in ./vibe-coder-data/), so down -v is safe. If your setup also has named volumes from another tool, use docker volume ls to check before running it.

Disk usage diagnosis

sudo du -sh vibe-coder-data/*/
#
# 1.2G  vibe-coder-data/dev-tools
# 8.4G  vibe-coder-data/workspace
# 120M  vibe-coder-data/server
# 80M   vibe-coder-data/postgres
# 8.0K  vibe-coder-data/claude

Inside dev-tools/:

sudo du -sh vibe-coder-data/dev-tools/*/
# 3.7G  android-sdk
# 1.8G  gradle
# 420M  npm-global
# 280M  npm-cache
# 310M  playwright
# 4.0K  config

Clone this wiki locally