Deploy your app
You built something that works on your laptop, but "works on my machine" helps no one. Deploying puts your app on the internet with a real URL, HTTPS, and env-based secrets so actual people can use it — and so every future change ships safely instead of breaking prod.
In 2026 you do not hand-configure servers. You pick a managed platform, connect your Git repo, and let a push to `main` build and deploy automatically — git-push-to-deploy is the whole mental model. Vercel for frontend/Next.js, Railway/Render/Fly.io for full-stack apps and containers. Your job is to make the app configurable through environment variables (never hardcoded secrets), keep `main` always-deployable, and rely on preview deployments to test PRs before they hit production. Treat the platform as the source of truth for prod config; treat your repo as the source of truth for code.
Step by step
- 1
Pick a platform that matches your stack
Next.js or a static/SPA frontend: Vercel or Cloudflare Pages. Full-stack app with a backend, background jobs, or a database: Railway or Render (both give you a URL from a Git repo in minutes). Need containers or edge regions: Fly.io. Don't overthink it — you can move later.
- 2
Get your code on GitHub first
Every one of these platforms deploys from a Git repo, not from your hard drive. Push to GitHub, confirm your `.gitignore` excludes `.env`, `node_modules`, and build output, then double-check the repo has no secrets committed in its history.
- 3
Connect the repo and confirm the build command
Import the repo in the platform's dashboard. The critical settings are the build command (usually `npm run build`), the output directory, and the start command — these must match your `package.json` scripts exactly, or the build fails or serves nothing.
- 4
Move every secret into environment variables
Database URLs, API keys, and auth secrets go in the platform's Environment Variables / Secrets panel, not in code. Read them via `process.env.X`. Set them for both production and preview environments so preview deploys actually work.
- 5
Turn on preview deployments
Vercel, Railway, and Render automatically build a throwaway deploy for every pull request with its own URL. This is your single best safety habit: test the PR's live URL before merging, and keep `main` as production.
- 6
Add your custom domain and HTTPS
Point your domain at the platform (usually a CNAME or the platform's nameservers). HTTPS certificates are issued and renewed automatically — you never touch a cert. Verify the app loads on the real domain, not just the platform's `*.vercel.app` URL.
- 7
Handle database migrations and health checks
Run migrations as a release/deploy step (e.g. `drizzle-kit migrate` or `prisma migrate deploy`), never a manual step you'll forget. Add a `/health` endpoint so the platform knows when a deploy is actually ready, and never point a seed script at production.
- 8
Know your rollback before you need it
Every platform keeps previous deployments and lets you re-promote one with a click. Find that button now. A bad deploy should be a 30-second rollback, not a 2am debugging session.
Starter snippet
# Production Dockerfile for a Next.js app (works on Railway, Render, Fly.io).
# Requires `output: 'standalone'` in next.config.js.
# syntax=docker/dockerfile:1
FROM node:22-alpine AS base
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM base AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup -g 1001 nodejs && adduser -S -u 1001 -G nodejs nextjs
COPY --from=build /app/public ./public
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]✕ Watch out for
- Committing a `.env` file or an API key to Git. Even if you delete it later, it lives in your history forever — rotate the key and scrub it. Add `.env` to `.gitignore` before your first commit.
- Hardcoding `http://localhost:3000` or a dev database URL. In prod those point nowhere. Every environment-specific value must come from `process.env`, and every var must be set in the platform's dashboard.
- The build passes locally but fails in CI. Usually a Node version mismatch or a case-sensitive-filesystem bug (`import './Button'` when the file is `button.tsx`) — Linux CI cares, your Mac doesn't. Pin your Node version and match filename casing exactly.
- Running migrations or seed scripts by hand — or worse, running a seed against prod. Migrations belong in a release step; a destructive seed pointed at the wrong `DATABASE_URL` will ruin your day.
- Shipping straight to `main` with no preview and no rollback plan. When (not if) a deploy breaks, you want a preview URL that caught it and a one-click re-promote of the last good build.
✓ Pro tips
- Tell your AI agent the exact platform and stack ('deploy this Next.js app to Railway with a Postgres DB'), then make it do two things: grep the whole codebase for `process.env.` and hand you the complete list of env vars the app reads, and confirm the build/start commands in its config match your `package.json` scripts. Missing env vars are the #1 cause of a deploy that builds fine but crashes on boot.
- Deploy on day one, before the app does anything real. A 'hello world' that's live is worth more than a finished app that's never been deployed — it proves the whole pipeline works while there's nothing to debug.
- Treat `main` as production and never push directly to it. Open a PR, look at the preview deployment's live URL, then merge. This one habit catches most 'it worked locally' failures.
- Ask the agent to add a `/health` endpoint and a rollback note to your README so future-you (and the platform) know how to tell a good deploy from a bad one.