Appearance
Migrating the Docker stack from the PC to the VPS
TL;DR — Don't transfer the Windows containers. Recreate the stack on the VPS from source (
docker compose up --build). Only transfer the data (Postgres + card images) if you want to skip the ingest step.
Why recreate instead of transfer
You have two options:
| Option | Description | Verdict |
|---|---|---|
| A. Transfer the containers | docker save images + copy volumes over SSH | Avoid |
| B. Recreate from source (IaC) | git clone + docker compose up --build on the VPS | Recommended |
Recreate (option B) — why it's better
- Everything is already reproducible —
compose.yml, the*.Dockerfiles and the source live intcg-collector-backend/. The VPS already knows how to build the stack. - Lighter — you transfer neither the ~5 GB of Docker images nor the buildkit caches. The VPS only pulls the bases (
pgvector/pgvector:pg16,nginx:alpine,node:22-alpine) and builds the custom layers from a few-MB repo. - Cleaner — no accumulated stale layers (the
tcg-collector-ingest-1container exited 5 days ago, the olderembeddingssidecar is likely replaceable, thesolidity-dojo_defaultnetwork is orphaned… it all starts fresh). - Avoids OS pitfalls — copying a raw
data/pgfrom Docker Desktop (WSL2) to Linux works in practice but stays fragile (pg_dumpis the safe path, see below). - Future-proof — every change later goes through
git pull && docker compose up -d --buildon the VPS, not another manual transfer.
Transfer (option A) — when it's worth it
Only if all of these are true:
- You modified application state by hand (data outside ingest).
- Re-downloading from TCGdex would be too slow or rate-limited from the VPS.
- You need to ship the service quickly.
In that case, do not transfer the containers — only the data: pg_dump + tar of the images (see §3).
1. Prepare the VPS
Prerequisites assumed to be in place on the VPS:
- Docker Engine + compose plugin installed.
- User
kaelin thedockergroup. ghinstalled and authenticated.- Repo
snag-tcg/tcg-collector-backendclonable.
Sanity-check first:
bash
docker compose version
docker run --rm hello-world2. Recreate the stack — happy path
bash
# 1. Clone (if not already done)
mkdir -p ~/skn-tcg && cd ~/skn-tcg
gh repo clone snag-tcg/tcg-collector-backend
cd tcg-collector-backend
# 2. Configure the environment
cp .env.example .env
# Edit .env:
# - PGUSER / PGPASSWORD (a STRONG password, not "tcg")
# - API_WRITE_TOKEN (random token if /scan or writes are exposed)
# - CORS_ORIGIN (the front-end domain, not "*" in production)
nano .env
# 3. Bring the stack up
docker compose -f docker/compose.yml up -d --build
# 4. Verify
docker compose -f docker/compose.yml ps
docker compose -f docker/compose.yml logs -f api
curl http://localhost:8080/health # or via nginx: :8081/api/health
# 5. First ingest (long — depends on INGEST_SETS)
docker compose -f docker/compose.yml run --rm ingestExpected build time: ~5 to 15 min depending on the VPS CPU (the longest is tcg-vision, which downloads ~85 MB of DINOv2 weights and builds CPU PyTorch).
3. Transfer the data (optional — if you want to skip the ingest)
Do this after the stack is running on the VPS, replacing only the Postgres contents and the data/images folder.
3.1 Postgres — pg_dump (clean, recommended)
On the Windows PC:
powershell
# Full dump (compressed, custom format)
docker exec tcg-collector-postgres-1 `
pg_dump -U tcg -d tcg -Fc -Z 9 -f /tmp/tcg.dump
docker cp tcg-collector-postgres-1:/tmp/tcg.dump .\tcg.dump
# Send to the VPS
scp .\tcg.dump kael@VPS_IP:/tmp/tcg.dumpOn the VPS:
bash
# Stack already running, but empty
docker cp /tmp/tcg.dump tcg-collector-postgres-1:/tmp/tcg.dump
# Restore
docker exec -i tcg-collector-postgres-1 \
pg_restore -U tcg -d tcg --clean --if-exists --no-owner /tmp/tcg.dumpWhy not just copy
data/pg? It often works (samepg16version), but it's fragile: differences in endianness, locale, page size, or apg_controlcorrupted mid-copy.pg_dumpis portable and version-tolerant.
Note on the index corruption detected in the PC's
data/pg(seecontainer-inventory.md):pg_dumpreads tables logically, so the dump regenerates clean indexes on the VPS. That's a clear win over a raw copy.
3.2 Card images — tar + scp (or rsync)
The ~1.9 GB of visuals are static, regenerable files, but slow to re-download.
powershell
# On Windows, from tcg-collector-backend\
tar -czf data-images.tar.gz data\images
scp data-images.tar.gz kael@VPS_IP:/tmp/On the VPS:
bash
cd ~/snag-tcg/tcg-collector-backend
tar -xzf /tmp/data-images.tar.gz # recreates data/images/
sudo chown -R $USER:$USER data/imagesThe compose's nginx service already mounts ../data/images; a simple docker compose restart nginx makes it live.
3.3 pHash & FAISS indexes
- The pHash index (
scanIndex.json) is generated in the siblingpoc-tcg-collectorrepo (npm run ingest:cards). No need to migrate it: regenerate on the VPS if you also deploy the RN client there, otherwise leave it on the build machine. - The
tcg-visionsidecar's FAISS index is prebuilt into the Docker image (docker/tcg-vision.Dockerfileclones a pinned commit ofKaelSensei/tgc-app). No manual action.
4. Should the older embeddings sidecar stay?
Probably not — tcg-vision replaces it functionally with a more complete approach. Before removing it:
- Grep the API source: is
EMBEDDINGS_URLstill read? - If yes, does it point at the older
embeddingsor attcg-vision? - If the older one is no longer consumed, remove the
embeddingsservice fromcompose.ymlfrom the start on the VPS. Savings: ~1.25 GB of image and some RAM.
5. Production — minimum security checklist
To configure on the VPS before any public exposure:
.env: strong Postgres password, long randomAPI_WRITE_TOKEN,CORS_ORIGINrestricted to the front-end domain.- Do not expose port
5432publicly. Incompose.yml, bind it to localhost explicitly:yamlports: - "127.0.0.1:5432:5432" - TLS reverse proxy (Caddy / Traefik / host nginx) in front of
:8080and:8000. No plaintext API. - UFW / iptables: open only
22/tcp(SSH) +80/tcp+443/tcp. - Rate-limiting in nginx or Fastify (
API_RATE_LIMIT_MAX). docker compose ... up -dat boot via systemd (therestart: unless-stoppedin compose is enough after a firstup).
6. Recommended order of operations
- Repair the index corruption on the PC (optional — to have a clean state to dump).
- On the VPS: complete the provisioning prerequisites listed in §1, then clone the repo.
cp .env.example .env+ edit secrets.docker compose up -d --build(lets Postgres + nginx + api + tcg-vision run empty).- Choose:
- Without transfer:
docker compose run --rm ingest→ let it run ~30 min. - With transfer:
pg_dump+pg_restore+tar/scpimages (§3).
- Without transfer:
- Health checks:
curl /api/health,curl :8000/health, scan a test card. - Move private ports behind a TLS reverse proxy (§5).
7. Once it's live — recurring workflow
No more transfers. The pattern becomes:
bash
# On the VPS
cd ~/snag-tcg/tcg-collector-backend
git pull
docker compose -f docker/compose.yml up -d --buildIf a new ingest is needed (new sets): docker compose run --rm ingest.