Internal reference — last verified against the live deploy
Mediabase — architecture
How the pieces actually connect: Loopia, the domain, Coolify, the database, and the app. Written from the real deployed system, including the two things that broke and why.
The full picture
Walking through each connection
1. Domain → server
Loopia holds one DNS A record: the subdomain media under solutionsskovde.se points at 89.167.80.108, the Hetzner VPS's public IP. That's Loopia's entire job here — it doesn't touch anything after the browser has the IP.
2. Request path
Every request for media.solutionsskovde.se lands on Coolify's built-in proxy (Traefik), which terminates SSL (a Let's Encrypt certificate, auto-renewed) and forwards it to the app container's internal port 3000. There's no separately configured Nginx — Coolify's proxy is the only thing in that role, on purpose (see STACK.md).
3. The app talks to two things
- Postgres, for everything in the
Mediatable — via Prisma, using the internal connection string set as theDATABASE_URLenvironment variable in Coolify. The database container has no public access; only other containers on the same Docker network can reach it. - The local filesystem, for the actual audio/video bytes, served directly by Next.js's static file handling from
public/media.
4. Media storage — a bind mount, not the Docker image
public/media/ is deliberately .gitignored — a 600MB video has no business going through GitHub. Instead, Coolify has a directory mount configured on the app: the host path /data/coolify/mediaplatform/media is bind-mounted into the container at /app/public/media. New files land by scp-ing them straight to that host path from your Mac (there's an SSH shortcut for this: ssh mediaplatform).
5. Deploy path
Pushing to main on GitHub doesn't trigger anything by itself yet — deploys happen when you click Deploy in Coolify, which pulls the latest commit and builds a fresh image using Railpack (Coolify's builder), then swaps the running container for the new one.
Two things that broke, and why
The build failed with Module not found: '@/app/generated/prisma/client'. Locally, npm install auto-generates the Prisma client via a postinstall hook — but Railpack skips install scripts (a common security/reproducibility default for build platforms), so that hook never ran. Fix: package.json's build script now explicitly runs prisma generate && next build, so generation never depends on an implicit hook.
After the first file upload, every play button was greyed out — the browser's media requests returned Next.js's own 404 page. The files were confirmed present, correctly permissioned, inside the container. The cause: the app's Node process had already started (and Next.js appears to do its static-asset resolution relative to that running process) before the scp transfer finished writing files into the mounted folder. Restarting the container — no rebuild needed — fixed it immediately. Takeaway: after adding new files to the mounted media folder, restart the app container before expecting them to be servable.
Known manual steps (not yet automated)
- Schema changes — after changing
prisma/schema.prismaand deploying, migrations don't run automatically. Run them by hand:ssh mediaplatform 'docker exec <container-name> npx prisma migrate deploy'. - New media files —
scpthem to/data/coolify/mediaplatform/media/<sound|video>/on the VPS, run the seed script inside the container (npx --yes tsx prisma/seed.ts), then restart the container so Next.js picks the files up. - Container name changes on every deploy — it's suffixed with a build hash, so re-check the current name with
docker psbefore running the two commands above.
Quick reference
| Thing | Where |
|---|---|
| Live site | https://media.solutionsskovde.se |
| Coolify dashboard | http://89.167.80.108:8000 |
| SSH to the VPS | ssh mediaplatform (alias in ~/.ssh/config) |
| SSH key | ~/.ssh/mediaplatform_hetzner — never committed to git |
Production DATABASE_URL | Coolify → app → Environment Variables (not in this repo) |
| Media on the VPS host | /data/coolify/mediaplatform/media/ |
| GitHub repo | github.com/Vulkanmannen2/mediabase |