We've covered why Socket.IO needs a persistent server, back in Topic 6. Now let's turn that into an actual deployment plan, and look at where our custom server can actually run in production.
Why Vercel Is Off the Table
- This is worth stating plainly, since it surprises a lot of developers coming from a typical Next.js background. Vercel does not support custom servers. Since our entire project runs through
server.ts, a custom Node.js server wrapping both Next.js and Socket.IO, we cannot deploy this specific setup to Vercel. This is not a Socket.IO limitation, it's a direct consequence of the custom server architecture we chose back in Phase 2.
- If you want to deploy Next.js on Vercel and still use Socket.IO, the common pattern is to keep Next.js on Vercel and run a single, separate Node.js process for the socket layer on a different platform entirely, with your frontend connecting to it as a client. That's a valid architecture, but a different one from what we've built in this course. For our project, since Next.js and Socket.IO share one process, we need a host that runs long-lived Node.js servers.
Where Our App Can Actually Run
Next.js can be deployed as a Node.js server, Docker container, or adapted to run on different platforms, and can be deployed to any provider that supports Node.js or Docker containers. For a project with a custom server, the realistic options are platforms built around long-running processes rather than serverless functions:
- Railway — Git-to-deploy workflow, straightforward for a project like ours
- Render — similar developer experience to Railway, with Web Service deployment supporting Next.js, managed TLS through Let's Encrypt
- Fly.io — runs your app on VMs, giving you persistent compute rather than spinning functions up and down, with strong control over deployment regions
- A plain VPS or Docker container — full control, more setup responsibility
There isn't one universally "correct" choice here, it depends on your budget, how much infrastructure you want to manage yourself, and how much traffic you expect. For a learning project or a small production app, Railway or Render are usually the simplest starting points. For more control over regions and scaling behavior, Fly.io is a common choice among teams self-hosting Next.js.
Preparing the Build
Regardless of platform, the deployment flow follows the scripts we already set up back in Topic 7:
npm run build npm run start
npm run build runs next build, producing the optimized production build. npm run start then runs our server.ts file with NODE_ENV=production, which serves that build through our combined Next.js + Socket.IO server.
A Basic Dockerfile
Most of these platforms deploy cleanly from a Dockerfile. Here's a minimal one suited to our project structure:
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./ RUN npm ci
COPY . . RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "start"]
This installs dependencies, builds the Next.js app, and starts the custom server on container startup. Environment variables, like JWT_SECRET or REDIS_URL from earlier posts, should be set through your hosting platform's environment variable settings, never committed into the Dockerfile or your repository.
Environment Variables to Set in Production
Based on everything we've built so far in this course, your production environment needs at minimum:
NODE_ENV=production PORT=3000 JWT_SECRET=your-production-secret REDIS_URL=redis://your-redis-host:6379
Never reuse a development JWT secret in production, and make sure your Redis instance, if you're using the adapter from the last post, is on a private network your app can reach, not exposed publicly, as we noted in Topic 22.
Connecting Sticky Sessions, If You Scale
If you eventually run multiple instances of this app behind a load balancer, revisit Topic 21: your hosting platform's load balancer needs sticky sessions enabled, so a client's requests consistently reach the same instance. Most platforms mentioned above support this through their load balancer configuration, though the exact settings differ per platform, so check your specific provider's documentation when you reach that point.
A Note on What You Give Up
A custom server also disables some automatic Next.js optimizations that assume the default server, and connection limits, memory management, health checks, and graceful shutdown all become your responsibility rather than being handled for you. This is the trade-off we accepted back in Topic 6, when we chose the combined custom server approach for this course. For a learning project, this is a completely reasonable trade-off. For a larger production application, it's worth revisiting occasionally whether splitting Socket.IO into its own dedicated service, as mentioned earlier in this post, makes more operational sense as your app grows.
Health Checks
Most hosting platforms expect a way to verify your app is alive. Since our server.ts already handles all incoming requests through Next.js, any normal page route works as a basic health check target, but it's often worth adding a dedicated, lightweight one:
if (req.url === "/health") { res.writeHead(200); res.end("OK"); return; }
Add this near the top of your request handler in server.ts, before requests are passed to Next.js, so your hosting platform can quickly confirm the server is responsive without going through the full Next.js rendering pipeline.
Summary
- Our custom server architecture rules out Vercel, since it does not support custom servers; a Node.js-friendly host is required instead
- Railway, Render, Fly.io, and plain VPS/Docker setups are all realistic choices, each with different trade-offs around simplicity and control
- Deployment follows
npm run buildthennpm run start, the same scripts we set up in Phase 2 - Environment variables like
JWT_SECRETandREDIS_URLbelong in your platform's environment settings, never in your repository - If scaling to multiple instances, sticky sessions must be configured at your load balancer, on top of the Redis Adapter from the last post
- A custom server takes on responsibilities Next.js normally handles automatically, including health checks and connection management
In the next post, we cover performance tips and common mistakes to avoid, closing out Phase 6 before we build the final project in Phase 7.
No comments:
Post a Comment