Lesson 12 of 12 · Ship it

Deploy it to the internet with Vercel and a hosted Postgres

Connect GitHub to Vercel, add a hosted Postgres database, run the migration in the cloud, and open your app on your phone. Then what to build next.

40 min You end with: Your client tracker live on a real URL, with a production database, deploying automatically on every push.

Everything you built runs on your laptop. This lesson moves it to a server you never have to touch, with a database you never have to install, on a URL you can open on your phone. Vercel handles the hosting. A hosted Postgres from Vercel’s marketplace handles the data. GitHub is the bridge: every time you push, Vercel builds and deploys the new version.

Before you start: Lesson 11 is done, everything is committed and pushed, and github.com shows your latest commit. Everything running (Lesson 6, “Stopping and coming back”).

What changes, and what does not

On your laptop: Docker runs Postgres, .env points at it, npm run dev runs the app.

On the internet: Vercel runs the app, a hosted Postgres holds the data, and Vercel holds the connection string for you as a setting. Your code does not change. Only where it runs and what it points at. That is why you kept the connection string out of the code in Lesson 8.

Three things to be clear on before you click anything:

  • The production database starts empty. Your local clients stay on your laptop. That is correct.
  • There is no login. Anyone with the URL can see and edit everything.
  • On Vercel’s free plan, the production URL cannot be locked. Password protection is a paid feature. So for now the deployed app is a demo with fake clients in it, not a place for real client data. That is fine for what this lesson is proving. Adding a login is the first project after this course, and until then, nothing real goes in.

Step 1: Create a Vercel account and deploy once

  1. Go to vercel.com/signup and choose Continue with GitHub. Approve the connection. Pick the free Hobby plan.
  2. On the dashboard, click the button to add a New Project.
  3. Vercel lists your GitHub repositories. If client-tracker is not there, click the link to adjust GitHub permissions and grant access to that repository.
  4. Click Import next to client-tracker. The next screen shows the framework detected as Next.js. Leave everything as it is.
  5. Click Deploy. The build runs for a minute or two. It may finish and show a page with a database error, or the build may fail with a database error. That is expected. There is no database yet. Clicking Deploy is what creates the project so you can add one.

Step 2: Add a hosted Postgres

  1. Open the project in Vercel and click the Storage tab.
  2. Click Create Database and choose Neon from the list. Neon has a free plan, and its Postgres is the same Postgres you ran in Docker.
  3. Follow its short setup. Accept the defaults for region and name. When it asks which environments to connect, keep all of them selected.
  4. When it finishes, Vercel adds a set of settings to your project automatically. Look under Settings → Environment Variables. The two that matter are DATABASE_URL (the address the app uses while running) and DATABASE_URL_UNPOOLED (a direct address that tools like Prisma’s migrations need).

Those are the production versions of the line in your .env. They have real passwords. They live in Vercel’s settings and nowhere else. Never paste them into a file in the project.

Step 3: Make the deploy run your migration

Your production database is empty: no Client table. The migration from Lesson 9 has to run there before the app can work. Back in Claude Code, plan mode on:

Prepare this project for deployment to Vercel with a Neon PostgreSQL database. Vercel provides two environment variables there: DATABASE_URL (a pooled connection for the app at runtime) and DATABASE_URL_UNPOOLED (a direct connection for migrations).

1. Configure Prisma so the app uses DATABASE_URL at runtime and migrations use DATABASE_URL_UNPOOLED, following the current Prisma docs for whichever Prisma version this project has. Locally, both can point at the Docker database; add DATABASE_URL_UNPOOLED to .env and .env.example accordingly.
2. Make the production build run the pending migrations before building the app, so the Client table exists on first deploy and future migrations apply automatically.
3. Make sure the Prisma client is generated during install or build on Vercel.
4. Check that nothing in the project assumes localhost or the Docker database.
5. Do not commit any secrets. Tell me the plan first.

Read the plan. It usually adjusts the build script in package.json, possibly adds a postinstall step, and touches the Prisma configuration. Approve with manual edits. Then:

Run the tests and the production build locally once more to confirm nothing broke. Show me what would be committed and confirm .env is not in it. Then commit with the message "Prepare for Vercel deploy" and push.

Step 4: Watch it deploy

The push you just made triggers a new deployment on its own. In Vercel, open the Deployments tab and click the one at the top.

The build log scrolls for a minute or two. You want to see the migration apply and then the Next.js build finish. When it says Ready, click the URL. Your app. On the internet. Open it on your phone.

Add a client. Make it obviously fake. It saves. Refresh. Still there. That row is in a Postgres database in a data center, not on your laptop.

Step 5: Prove the pipeline, and write it down

The point of connecting GitHub is that you never “deploy” again. You push, it deploys. Prove it with a change that is worth making anyway:

Add a "Deploying" section to README.md explaining, for a non-developer: the app is hosted on Vercel, the database is Neon Postgres from the Vercel marketplace, DATABASE_URL and DATABASE_URL_UNPOOLED live only in Vercel's environment settings, migrations run during the build, every push to the main branch deploys, and there is no login yet so only fake data goes in. Commit and push.

Go to Vercel → Deployments. A new one started on its own. When it is Ready, that is the pipeline working.

You are done. Here is what you built.

  • A Next.js web app with a real screen you specified, tested like a user, and corrected one change at a time.
  • A Postgres database you can run locally in Docker and reset with one command.
  • Prisma as the translator, with a migration history that recreated the table in production without you touching a database console.
  • Git history you can roll back, backed up to GitHub, with no secrets in it.
  • Server-side validation, four tests, a production build that passes, and a README a stranger could follow.
  • A live URL that redeploys itself on every push.

You did not write code. You did the job of the person the code is for, and you did it with the habits that keep it from falling over.

What to build next

In rough order of usefulness:

  1. A login. The most requested next step and the thing standing between “demo” and “real.” Ask Claude Code, in plan mode, to compare the mainstream authentication options for a Next.js app and recommend one for a single-team tool. Commit before you start, and treat it as its own project.
  2. Backups. Ask: “How is the production database backed up, and how would I restore it?” The answer depends on your provider and it is worth knowing before you need it.
  3. A second screen. Client detail pages with a history of notes. Same process: plan, build with fake data, connect, test, commit.
  4. A custom domain. Vercel → Settings → Domains. Ten minutes.

Where it breaks

Everything in this course worked on the first app, in a quiet week, with one user. What comes next is real users, a feature you did not plan for, an AI edit that quietly changed how login works, a bill you did not expect. Every one of those has been cataloged from real apps in What breaks, and the blog covers the fixes: exposed API keys, the AI deleting your code, deployment mistakes, what “not production ready” actually means, and what happens when your app goes viral.

That is what Red Corner is for: a sitting CTO in your corner, in chat, in group sessions, and on a call every month, once you are shipping every week instead of once. The button below is where it starts.

Check your work

  • The app opens on your phone at a vercel.app URL.
  • Adding a fake client on the live site saves it, and it is still there after a refresh.
  • .env is still not on GitHub, and no real connection string is in any file.
  • A push to GitHub triggered a new deployment without you clicking anything.
  • The README explains the deployment and says only fake data goes in until there is a login.

If something went wrong

The build fails on Vercel with a database error after Step 3. Check Settings → Environment Variables: both DATABASE_URL and DATABASE_URL_UNPOOLED should be there for Production. If they are, paste the build log into Claude Code: “This is the Vercel build log. What failed, and what do I change?”

The build succeeds but the page shows a database error. The migration did not run, so the table does not exist. Ask Claude Code to “show me how to run the pending migrations against the production database from my laptop, using DATABASE_URL_UNPOOLED from Vercel’s settings without saving it to any file.” Follow the steps once; then fix the build so it happens automatically next time.

Vercel cannot see my repository. GitHub app permissions. In Vercel, Settings → Git, or during import, click the link to configure the GitHub app and grant access to client-tracker.

The deploy works but it is slow the first time each morning. Free-tier databases and functions sleep when idle. It is a free-tier trade-off, not a bug. It goes away on paid tiers.

Something else, and you have been at it for twenty minutes. Habit 3. The button is right below.

Did this lesson not go to plan?

A CTO can help you starting today.

Screenshot the error, note which step you were on, and request a call. Every member starts with a call: bring what you built and where it stopped, and that is where we work out whether we can help and what to do first. No card. No sales deck.

Request a call with your CTO

Bring it to a CTO.

The error, the screenshot, or the thing you are afraid to touch. Every member starts with a call, and that is where we work out whether we can help and what to do first.

Request a call with your CTO

Every member starts with a call. No card.