Files
starr-philproperties/SELF_HOSTING.md
T
kennethobsequio 51810cad21 latest docs
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
2026-08-29 16:36:40 +08:00

7.9 KiB

Self-Hosting new_starr (Docker Compose) — Step-by-Step

Video tutorial soon

This stack bundles everything: PostgreSQL, Valkey (Redis), Garage (S3), the API, and the frontend. Battle-tested end-to-end on a completely fresh Debian machine on 2026-08-29 every gotcha below actually happened once and is now fixed or documented.


0. What you need

  • Docker Engine + Compose plugin
  • Node.js 22 + pnpm (only needed once, to build the frontend bundle)
  • Git access to this repo

1. Install Docker

Check your actual distro first — Docker has separate repos for Debian and Ubuntu, and mixing them up 404s:

cat /etc/os-release   # check ID (debian/ubuntu) and VERSION_CODENAME
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

(Swap debian → ubuntu in both URLs if you're actually on Ubuntu. If your codename is brand-new and Docker's repo 404s, fall back to the previous stable codename — e.g. use bookworm instead of trixie.)

Let your user run Docker without sudo:

sudo usermod -aG docker $USER
newgrp docker
docker compose version   # sanity check

2. Clone & install

git clone git@github.com:rgrgogu/new_starr.git
cd new_starr
corepack enable && corepack prepare pnpm@11.3.0 --activate
pnpm install

3. Configure environment — three files, not two

apps/api/.env

cp apps/api/.env-production apps/api/.env

Fill in every CHANGE_ME. For a local bundled stack (as opposed to CockroachDB Cloud / managed S3), these specific values matter:

DB_HOST=postgres
DB_PORT=5432
DB_SSL=false                          # bundled Postgres has no TLS
CACHE_DRIVER=redis
REDIS_URL=redis://valkey:6379
S3_ENDPOINT=http://garage:3900
GARAGE_RPC_SECRET=<openssl rand -hex 32>

Generate every secret independently:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

apps/web/.env

cd apps/web && cp .env.example .env

Fill in VITE_API_URL, VITE_APP_URL, etc., then cd ../...

Root new_starr/.env — easy to miss, causes silent failures

docker-compose.yml's bundled postgres service reads ${DB_NAME}, ${DB_USER}, ${DB_PASSWORD} from a root-level .env — NOT from apps/api/.env. Create it separately, with exactly the same values as the DB block you just put in apps/api/.env:

DB_NAME=starr
DB_USER=starr
DB_PASSWORD=<same password as apps/api/.env>

⚠️ Postgres only reads these once, at first-ever container start, and bakes them into its data volume. If the two files ever drift apart afterward, you'll get password authentication failed even though everything "looks" configured — fix by making apps/api/.env match what's actually in the volume (the root .env value at the time Postgres first started), not the other way around.


4. Build the frontend bundle before building images

apps/web/Dockerfile only copies a pre-built dist/ — it does not run the build itself:

pnpm --filter web build

5. Bring the stack up

docker compose up -d --build
docker compose ps       # all 5 services should show Up/healthy

6. One-time Garage (S3) cluster init

Garage's image ships only a static binary — no shell — so this can't be scripted as a container entrypoint; it's a manual one-time step:

docker compose exec garage /garage node id

Copy the ID (the part before the @host:port), then:

docker compose exec garage /garage layout assign <NODE_ID> -z dc1 -c 100G
docker compose exec garage /garage layout apply --version 1
docker compose exec garage /garage bucket create <S3_BUCKET>
docker compose exec garage /garage key import <S3_ACCESS_KEY> <S3_SECRET_KEY> -n starr-app --yes
docker compose exec garage /garage bucket allow <S3_BUCKET> --read --write --owner --key <S3_ACCESS_KEY>

(S3_BUCKET/S3_ACCESS_KEY/S3_SECRET_KEY are whatever you set in apps/api/.env.)


7. Run database migrations

docker compose exec backend sequelize-cli db:migrate

8. Verify

docker compose ps

If both respond, you're done with local testing.


9. (Optional) Put a real domain + HTTPS in front

For actually deploying this somewhere reachable, use the Caddyfile at the repo root as a template. It's a standalone Caddy install on the host (not part of docker-compose.yml) that reverse-proxies 3 domains to the 3 ports already published above (:4650, :3024, :3900) and handles Let's Encrypt certificates automatically. See the comments at the top of Caddyfile for exact steps. Not needed for local localhost:4650 testing — skip this until you're pointing a real domain at the machine.


Troubleshooting — real errors hit while writing this guide

Symptom Cause Fix
apt 404: .../linux/ubuntu <codename> Release not found Docker repo added for the wrong distro (e.g. Ubuntu URL on a Debian box) Match the URL to your real distro/codename from /etc/os-release
permission denied ... docker.sock User not in the docker group sudo usermod -aG docker $USER && newgrp docker
WARN: "DB_NAME" variable is not set (and similar) Root new_starr/.env missing Create it (see step 3)
postgres container stuck Restarting POSTGRES_PASSWORD blank (same root .env issue) Same fix as above
exec: "/bin/sh": stat /bin/sh: no such file or directory Some images (like Garage's) ship no shell at all — never wrap their entrypoint in /bin/sh Use the image's default command; do cluster setup via docker exec <container> <binary> directly (no shell needed)
Garage crash-loops: rpc_secret value is missing GARAGE_RPC_SECRET not set in apps/api/.env Set it, then docker compose up -d --force-recreate garage
password authentication failed for user ... apps/api/.env and root .env DB passwords don't match exactly Make them match; Postgres only honors whatever was set at first boot
Fixed a file but the same error keeps happening Images bake in code at build time — there's no live volume mount docker compose up -d --build <service> after any source change
ERROR: Unknown constraint error during migrate Sequelize hides the real Postgres error behind this generic message Run the raw SQL directly via docker compose exec postgres psql -U <user> -d <db> to see the actual cause
Migration uses STRING as a raw SQL type CockroachDB alias, not valid in real Postgres Already fixed in this repo (swapped to TEXT, which works on both)
A service vanishes from docker compose ps A previous up was scoped to one service (e.g. up -d --build backend), which doesn't touch others Run a plain docker compose up -d (no service name) to restore everything
Browser: "Unable to connect" Either the container isn't actually running, or you're browsing from a different machine than the one running Docker Check docker compose ps first; localhost only resolves to the machine Docker is on

Notes

  • Frontend is served on :4650, not :80 — change frontend.ports in docker-compose.yml if you want a different port.
  • Cluster init for Garage is intentionally manual (step 6) — the old automated garage-init.sh approach was removed because it depended on a shell that doesn't exist in the Garage image.