From Git Push to Live Website
Shipping software answers two questions: is the code correct? and does it run in production? Split those into separate tools — CI validates, Docker packages, a host runs.
Vocabulary
| Station | Tool | Job |
|---|---|---|
| Validate | GitHub Actions / GitLab CI | Lint, test, typecheck on every push |
| Package | Docker | Same runnable image everywhere |
| Run | Railway / Fly.io / Render | Host container, attach DB, set env vars |
CI does not deploy. The host does not skip tests.
Steps
Step 1 — CI on every push
A workflow file triggers on push and pull request. It installs deps, generates the Prisma client, and runs tests:
The dummy DATABASE_URL exists only so prisma generate succeeds — CI does not need a live database.
Step 2 — Build a Docker image
Multi-stage Dockerfile: install deps, build app, copy only what the runner needs:
Build locally: docker build -t myapp .
Step 3 — Start production with migrations
The startup script runs migrations before serving:
Step 4 — Deploy to a host
Connect your repo to Railway (or similar). Set environment variables in the dashboard:
DATABASE_URL— from a managed Postgres pluginBASE_URL— your public domain (https://yoursite.com)- API keys for email, auth, etc.
Never commit secrets to git.
Common pitfalls
- Running
migrate devin production — it can reset data. Usemigrate deploy. - Hard-coding
localhostin SEO links — setBASE_URLin production. - Skipping CI before merge — broken code reaches the host.
Verify it works
Run docker build and docker run locally with a .env file. Hit localhost:3000. Check logs for "Applying database migrations" before "Starting production server."
Takeaway
CI guards quality. Docker guards consistency. The host guards uptime. Secrets live in the platform dashboard — not in your repo.