Skip to main content

Command Palette

Search for a command to run...

How to Deploy a Node.js App to Render (And What to Know About Port Blocking)

Updated
7 min read
O
Full-stack engineer building real-world applications with Next.js and Vite. I share practical insights, projects, and lessons from my journey.

Introduction

You've built your Node.js backend. It works perfectly on your machine. Now what? Getting it live on the internet is the next step, and it's easier than most people think.

In this article I'll show you how to deploy a Node.js app to Render, how to handle environment variables, and what you need to know about port blocking so your app actually works in production. This is based on how I deployed my own Taskflow application, including getting Nodemailer working for email sending.

Render vs Railway

Before we start, here is a quick comparison of the two most popular free platforms for deploying Node.js apps.

Render gives you a free tier that keeps your app live, though it spins down after 15 minutes of inactivity on the free plan. It has a clean dashboard, automatic deployments from GitHub, and supports environment variables out of the box. It is what I use for Taskflow and it has been reliable.

Railway also has a free tier and is slightly faster to set up. It gives you a certain amount of free usage hours per month before charging. Both platforms support Node.js, PostgreSQL, and environment variables.

For this article we will use Render because it is what I have production experience with, but the concepts apply to Railway as well.

What You Need Before Starting

Make sure you have:

  • A Node.js app pushed to a GitHub repository

  • A Render account at render.com (free to sign up)

  • Your environment variables ready (from your .env file)

Preparing Your App for Deployment

Before deploying there are a few things your app needs.

The PORT Variable

On your local machine your app probably runs on a hardcoded port like 3000. On Render, the platform assigns the port dynamically through an environment variable called PORT. Your app needs to respect that.

Make sure your server listens like this:

const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})

The || 3000 fallback means it uses 3000 locally but picks up whatever port Render assigns in production. If you hardcode 3000 without the fallback your app will fail to start on Render.

The Start Script

Make sure your package.json has a start script:

"scripts": {
  "start": "node src/index.js",
  "dev": "nodemon src/index.js"
}

Render uses the start script to run your app in production. Not dev, not nodemon, just start.

Environment Variables

Never push your .env file to GitHub. Make sure it is in your .gitignore:

node_modules
.env

We will add the environment variables directly on Render in the next steps.

Deploying to Render

Step 1: Create a New Web Service

Log in to your Render dashboard and click "New" then select "Web Service".

Step 2: Connect Your GitHub Repository

Render will ask you to connect your GitHub account. Once connected, search for your repository and select it.

Step 3: Configure the Service

Fill in the settings:

  • Name: give your app a name like my-users-api

  • Region: choose the one closest to your users

  • Branch: main or master, whichever you use

  • Build Command: npm install

  • Start Command: npm start

  • Instance Type: select Free

Step 4: Add Environment Variables

Scroll down to the Environment section before clicking Deploy. This is where you add everything from your .env file.

Click "Add Environment Variable" for each one:

DB_USER=your_postgres_username
DB_HOST=your_db_host
DB_NAME=your_db_name
DB_PASSWORD=your_db_password
DB_PORT=5432
JWT_SECRET=your_secret_key
JWT_EXPIRES_IN=24h

Never skip this step. If your app tries to start without these values it will crash immediately.

Step 5: Deploy

Click "Deploy Web Service". Render will pull your code from GitHub, run npm install, and start your app. You will see the build logs in real time.

Once the deployment is complete Render gives you a live URL that looks like https://your-app-name.onrender.com.

The Port Blocking Issue

This is something that catches a lot of developers off guard when deploying for the first time.

Cloud platforms block certain network ports to prevent spam and abuse. The most commonly blocked port is port 25, which is the old standard SMTP port for sending emails. If your app tries to send emails through port 25 in production it will silently fail.

The ports that work on both Render and Railway are:

  • Port 587 with STARTTLS, which is the recommended port for most email services

  • Port 465 with SSL, which is used by Gmail SMTP and some other providers

In my Taskflow app I use Nodemailer to send emails and it works perfectly on Render because I am using port 587. Here is how the Nodemailer transporter is configured:

const transporter = nodemailer.createTransport({
  host: 'smtp.gmail.com',
  port: 587,
  secure: false,
  auth: {
    user: process.env.EMAIL_USER,
    pass: process.env.EMAIL_PASS
  }
})

secure: false tells Nodemailer to use STARTTLS on port 587 instead of SSL. If you set secure: true you need to use port 465 instead. Both work on Render. Just never use port 25.

If you are using Gmail, make sure you use an App Password instead of your actual Gmail password. Go to your Google Account, enable two-factor authentication, then generate an App Password specifically for your app. Store it as an environment variable and never commit it to your repository.

Automatic Deployments

One of the best things about Render is that once you connect your GitHub repository, every time you push to your main branch Render automatically redeploys your app. No manual steps needed.

This means your workflow becomes:

git add .
git commit -m "fix: update user validation"
git push origin main

And your live app updates within a couple of minutes.

Troubleshooting Common Issues

App crashes on startup: almost always means a missing environment variable. Check your Render logs and look for something like Cannot read properties of undefined. Go back to the environment variables section and make sure everything from your .env file is there.

502 Bad Gateway: usually means your app is not listening on process.env.PORT. Double check your server setup and make sure you are not hardcoding the port.

App spins down on free tier: Render's free tier spins down your app after 15 minutes of inactivity. The first request after it spins down takes 30 to 60 seconds while it wakes up. This is normal on the free plan. Upgrade to a paid plan if you need it always on.

Emails not sending: check which port you are using. Switch to 587 with secure: false or 465 with secure: true. Make sure your email credentials are set as environment variables on Render and that you are using an App Password if you are on Gmail.

Render vs Railway: Quick Comparison

Render Railway
Free tier Yes, spins down after inactivity Yes, limited monthly hours
Auto deploy from GitHub Yes Yes
PostgreSQL support Yes Yes
Port 25 blocked Yes Yes
Port 587 and 465 Open Open
Dashboard Clean, beginner friendly More technical

Both are great options. I use Render for Taskflow and it has been solid. If you want something slightly more developer focused with a faster setup, try Railway.

Conclusion

Deploying a Node.js app to Render is straightforward once you know the three things that matter most: use process.env.PORT for your server, add all your environment variables on the platform, and use port 587 or 465 for email sending instead of port 25.

Getting your app live is one of the most satisfying moments in building something. Once it's deployed, share the link, get feedback, and keep building.

Found this helpful? Follow me on Hashnode, connect with me on LinkedIn, and follow me on X for more articles on full stack development, Node.js, and backend architecture.