For years, Heroku’s free tier has been the standard by which many developers released prototypes, hobby projects, and portfolio pieces to the public.
Unfortunately, Heroku announced plans to retire its free tier beginning on November 28th, 2022, leaving many developers scrambling to find alternatives to host their Bootcamp and portfolio projects.
In this article, we’ll review several alternatives to using Heroku for deploying apps. We’ll build a simple, framework-free Node.js app, and then deploy it to three different services that offer a free tier, Render, Railway, and Fly.io. We’ll also explore some Heroku alternatives for static deployment and serverless functions. Let’s get started!
Jump ahead:
- Building our test app
- Deploying to Render
- Deploying to Railway
- Deploying using Fly.io
- Heroku alternatives for static deployment
- Serverless functions
- Paid options
Building our test app
We’ll build a plain vanilla Node.js server, but the same principles would apply if you’re using any of the popular Node.js frameworks, like Express, Koa, Fastify, NestJS, FoalTS, etc. You should already have Node.js installed on your machine.
Open your editor to an empty folder. Create a new Node.js project from your terminal using the npm init -y
command.
Create an index.js
file called touch index.js
. In package.json
, let’s set up our start script with the command below:
"scripts": { "start": "node index.js" },
Inside index.js
, run the following code to configure a basic “Hello, World!” Node.js server:
// Import http library const http = require("http") // use env variable to define tcp/ip port with a default const PORT = process.env.PORT || 8080 //create our server object const server = http.createServer() // We define a function that runs in response a request event server.on("request", (request, response) => { // handle request based on method then URL response.statusCode = 200 response.write("Hello World") response.end() }) // get the server to start listening server.listen(PORT, err => { // error checking err ? console.error(err) : console.log(`listening on port ${PORT}`) })
Let’s test this locally by running node start
and then heading to localhost:8080
. If you see “Hello, World!”, then it’s working, and you can begin deploying it. From your terminal, create a git repository called git init
. Add all of your files to staging with the git add .
command, then make a commit with git commit -m"First Commit"
.
Next, head over to GitHub.com, create a new repository, and get the URL. Add your GitHub.com repo as a remote to your local repo, and be sure to use your URL:
git remote add origin https://github.com/username/reponame
Then, push your code up to remote git push origin main
. You can confirm whether your branch is main or master with the command git branch
.
Now that we have our example code on GitHub, we can begin deploying it. For reference, you can check out my repo for this tutorial.
Deploying to Render
Render’s free tier makes static deployments for React, Vue, and Angular apps easy, as well as web service deployments for Node.js apps. Just follow these steps:
- Log in to Render.com using your GitHub account
- Click New > Web Service
- Connect the GitHub repository you just created
- On the next screen, give your project a name, accept all defaults and the free tier, and then deploy
From the following screen, we wait for the deployment to complete, which can take a while. In the upper left hand corner, you can find the URL for when it is done:
Once it’s done, click on the URL that was generated for you, and you should see our “Hello, World!” message. That was pretty easy, wasn’t it!
Render offers other services that you may find useful as you build your application, like databases and cron jobs, which are scripts that run on a set schedule.
Deploying to Railway
Similar to Render, Railway is a service that allows you to deploy your application and provision several types of databases, like Postgres, mySQL, and MongoDB. The steps to deploy our Node.js application on Railway will be very similar to those for Render:
- Log in using your GitHub account
- Create a new project and select deploy from GitHub
- Select your repo, then select deploy now
It should complete pretty quickly, and you’ll see the following screen:
By default, it will not have a publicly accessible domain name. To generate one, head over to the Settings tab and click Generate Domain; this will create a URL that you can use to share your project with others. That’s it!
Deploying using Fly.io
Fly.io focuses primarily on deploying Docker containers, and over the last few years, it has made the process much easier. First, sign up using your GitHub account. We’ll take a different approach when deploying with Fly.io:
- Install Fly.io’s command line tool, FlyCtl
- Log in with the command
flyctl auth login
. It will ask for payment info, but you won’t be charged as long as you remain within the free tier - From the terminal, deploy the app with
flyctl launch
- Answer the prompts and accept the defaults, then wait for the deployment to finish
It may take a while, but once it is deployed, you’ll see the application on your Fly.io dashboard as well as the URL where it is located.
If you need to update your app in the future, simply run the flyctl deploy
command from the directory where your application is. When you ran flyctl launch
, it created a fly.toml
file with all the deployment settings for speeding up updates, so it knows which deployment to update.
If it doesn’t work, double check that the port that is exposed in fly.toml
is the same one that is exposed in your index.js
on line 4 as the default port.
Heroku alternatives for static deployment
Not every application requires you to deploy a server; for example, frontend React, Vue, SolidJS, Svelte, and Angular apps can be deployed as static apps.
There are several options for deploying these static apps for free that only require you to connect your GitHub repository with your project, including Render, Railway, Fly.io, Netlify, GitHub Pages, Vercel, App by Digital Ocean, Surge.sh, Firebase, and Azure Static Apps.
Instead of persistently running server code on the providers’ severs, only the static HTML, CSS, and JavaScript files in your project are being delivered. Therefore, there are fewer free options.
Static deployment on Render
To deploy a static site on Render, go to your dashboard and add a new Static Site:
From here, you just have to connect the repository with your static website, specify any build commands like npm run build
for React, then click deploy.
Static site deployment on Netlify
Log in to Netlify.com, then click Add new site from the dashboard:
Then, select a GitHub repository:
Set your build command and publish directory, which will differ based on what frontend framework you use:
Finally, click Deploy site, and you’re done!
Static site deployment on Vercel
Log into Vercel; from the dashboard, click on Add New, then select Project:
Select a GitHub repository to deploy:
Set up your build command and output director, then click deploy:
Static site deployment on Digital Ocean’s App platform
Head over to the Apps section of the Digital Ocean dashboard, then click on Create and select Apps:
Select a GitHub repository with your application and pass it the necessary configurations, like the source directory:
On the next page, we‘ll set our app to deploy as a static site:
To deploy a backend service like Heroku, you can also select Web Service, but this is not free on Digital Ocean. After changing it to a static site, make sure to check the build command and output directory:
Click on Review and then Create Resource, and your app will be deployed!
Static site deployment on GitHub Pages
On any GitHub repository that holds a static site, just go into the Settings section and click on Pages:
Next, select a branch to deploy and save, and in a few minutes, the site should be live:
Serverless functions
Serverless functions are another option for handling backend database interactions and API delivery, which is what we would normally deploy server applications for.
Instead of writing your server that runs 24/7, you can write functions with your desired logic that run on demand on the provider’s servers. All of the major clouds, like AWS, Azure, and Google provide serverless functions as a service, but there are other services that make using serverless functions much easier.
Usually, these services require you to connect a GitHub repo with one file for each function you want to deploy, which is then assigned a URL that you can call from your frontend application.
Using serverless functions on Netlify
To add a bit of backend functionality into your static apps deployed on Netlify, simply create a /netlify/functions
folder in your repository .You can add as many JavaScript files in this folder as you’d like; for example, let’s imagine I created a /netlify/functions/cheese.js
file that looked like the following:
exports.handler = async function (event, context) { return { statusCode: 200, body: JSON.stringify({ cheese: "Gouda" }), }; };
When the code above is deployed to Netlify, it’ll create a route that you can make requests to at /.netlify/functions/cheese
. You can use any of the libraries loaded in the project’s package.json
, so connecting to databases and other typical backend activities are all fair game.
Using serverless functions on Vercel
To use serverless functions with static apps deployed on Vercel, you can use Vercel functions, which have a similar feel to ExpressJS when you use JavaScript.
Simply create your JavaScript or TypeScript files in a folder called /api
in your project. For example, let’s say I create a file called /api/cheese.js
that does the following:
export default function handler(req,res) { res.json({cheese: "gouda"}); }
When I deploy my application to Vercel, I can make any API request to /api/cheese
to get back the response from this function.
Paid options
Heroku’s main purpose was to deploy dynamic server applications. Beyond the free options from Render, Railway, and Fly.io, some other paid options work along the same lines, connecting your GitHub repo and releasing your app:
- App Platform from Digital Ocean: Starting at $5/mo
- AWS Amplify: Pricing depends on the cost of the AWS resources needed to run app
Conclusion
Yes, it’s a little disruptive that we’ve lost Heroku’s free tier, but the resulting splintering across many different services like Render, Railway, and Fly.io will make the industry more resilient. With developers no longer beholden to one vendor, the competition for market share will accelerate the drive to innovation:
Provider | Free static deployment | Free server deployment | Free serverless functions | Paid entry point |
---|---|---|---|---|
Fly.io | No | Yes | No | Metered prices |
Render | Yes | Yes | No | Starter projects $7/mo |
Railway | Yes | Yes | No | Metered pricing |
Heroku | No | No | No | $5/mo |
Netlify | Yes | No | Yes | $19/ mo |
GitHub Pages | Yes | No | No | None |
Vercel | Yes | No | Yes | $20/mo |
App by Digital Ocean | Yes | No | No | $5/mo enabling server/static |
Firebase | Yes | No | Yes | Metered pricing |
Surge.sh | Yes | No | No | $30/mo |
I hope you enjoyed this article. Be sure to leave a comment if you have any questions, and let me know which service you prefer as an alternative to Heroku. Happy coding!
200’s only
Monitor failed and slow network requests in production
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. 
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.