Published At: September 13, 2025
Reading Time: 4 minutes
Author: Prajwol Shrestha
So, you’ve built your NestJS app and now want to deploy it online, without spending any money or adding a credit card. In this blog, we’ll walk through a clear, step-by-step process to Dockerize your app and deploy it on Render for free.
Render’s free tier makes it easy to deploy Dockerized apps, while Docker ensures your project runs consistently everywhere. In this guide, we’ll:
pnpm.main.Let’s dive in! 🐳
Docker helps package your NestJS app with all its dependencies so it runs exactly the same in all environments.
Render supports two ways of deploying Docker apps:
We’ll cover both options.
First, we need to containerize our application. If you don’t have Docker installed, grab it here.
DockerfileCreate a file named Dockerfile in your project's root directory:
Here’s a example Dockerfile using pnpm:
1# Use a lightweight Node.js base image
2FROM node:20-slim
3
4# Set working directory
5WORKDIR /app
6
7# Copy dependency files first (better cache usage)
8COPY package.json pnpm-lock.yaml* ./
9
10# Install dependencies
11RUN npm install -g pnpm && pnpm install --frozen-lockfile
12
13# Copy the rest of the source code
14COPY . .
15
16# Build the NestJS project
17RUN pnpm run build
18
19# Expose the app’s port
20EXPOSE 3000
21
22# Start the app
23CMD ["pnpm", "run", "start:prod"]
24
.dockerignoreCreate a .dockerignore file to keep your image lean and mean by excluding unnecessary files:
1node_modules
2dist
3.git
4Dockerfile
5.dockerignore
6.env
Before we deploy, let's build and run the container locally to make sure everything works.
1docker build -t nest-backend .
2docker run -p 3000:3000 nest-backend
Head over to http://localhost:3000 to verify your app is running. If it works, you're ready for deployment! ✅
Now that your NestJS app is Dockerized, let's get it deployed on Render. We have two options:
This is the easiest way:
Dockerfile and build the image automaticallyThat's it! Render will now build your Docker image from the Dockerfile and deploy your app. Easy. 🎉
This method gives you more control and enables automatic deployments.
This also enables automatic CI/CD deployments on every push.
1# Build image
2docker build -t nest-backend .
3
4# Tag with Docker Hub username
5docker tag nest-backend your-username/nest-backend:latest
6
7# Push to Docker Hub
8docker push your-username/nest-backend:latest
On Render, choose Deploy an existing Docker image and use:
1docker.io/your-username/nest-backend:latest
Render will fetch the latest image whenever you trigger a redeploy.
📝 Notes on tags:
- Render redeploys based on the tag you specify (
latest,v1.0.0, etc.).- Using
latestis simple, but versioned tags (e.g.,v1.0.0) are safer for production since they make rollbacks easier.- Render only fetches a new image from Docker Hub when you trigger a redeploy (manually or via CI/CD).
Create .github/workflows/docker-build.yml:
1name: Build & Push Docker Image
2on:
3 push:
4 branches: [ "main" ]
5
6jobs:
7 build:
8 runs-on: ubuntu-latest
9
10 steps:
11 - name: Checkout repository
12 uses: actions/checkout@v3
13
14 - name: Log in to Docker Hub
15 uses: docker/login-action@v2
16 with:
17 username: ${{ secrets.DOCKER_USERNAME }}
18 password: ${{ secrets.DOCKER_PASSWORD }}
19
20 - name: Build and push Docker image
21 uses: docker/build-push-action@v4
22 with:
23 context: .
24 push: true
25 tags: your-username/nest-backend:latest
26
27 - name: Trigger Render Deploy
28 run: |
29 curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}
In your GitHub repo → Settings → Secrets → add:
DOCKER_USERNAME → your Docker Hub usernameDOCKER_PASSWORD → your Docker Hub access tokenRENDER_DEPLOY_HOOK → from Render dashboard (service → settings → deploy hook URL)Now every push to main will:
On Render’s free tier, your service goes to sleep after 15 minutes of inactivity to save resources. When that happens, the next request can feel slow because the app has to “cold start.”
To prevent this, you can set up a simple heartbeat that pings your app every few minutes.
You don’t have to use Cloudflare Workers — you could also use:
In this tutorial, we’ll go with with Cloudflare Workers (free tier).
Install Wrangler, Cloudflare’s CLI.
1npm install -g wrangler
Generate a new worker:
1wrangler init keep-alive-worker
In wrangler.toml:
1name = "keep-alive-worker"
2main = "src/index.ts"
3compatibility_date = "2025-09-13"
4
5# Add this
6[triggers]
7crons = ["*/15 * * * *"]
8
9# (Optional) to enable logs for worker
10[observability.logs]
11enabled = true
In src/index.ts:
1const SERVICES = [
2 'https://your-app-name.onrender.com', // Your app's URL
3 // Add more services here if needed!
4];
5
6export default {
7 async scheduled(event, env, ctx) {
8 for (const url of SERVICES) {
9 try {
10 const res = await fetch(url);
11 console.log(`✅ Pinged ${url} → ${res.status}`);
12 } catch (err) {
13 console.error(`❌ Failed to ping ${url}`, err);
14 }
15 }
16 },
17};
1wrangler deploy
That’s it! 🎉 Your Cloudflare Worker will automatically ping your Render app(s) every 15 minutes, keeping them awake and responsive.
#cicd
#nestjs
#docker
#render