Deploying Node.js Apps on AWS: Elastic Beanstalk Or App Runner?

Deploying Node.js Apps on AWS: Elastic Beanstalk Or App Runner?

AWS offers over 200 services, with each of them catering to different use cases. There are at least 5 of them for deploying web applications and API services, so it can be difficult to decide which service is best suited for your use case.

I recently built an API using Node.js and wanted to deploy using AWS Elastic Beanstalk - it is often the recommended choice for deploying Node.js applications and I was also familiar with it. However, I soon realised that it was not the best choice for my application and had to find an alternative, which led me to App Runner.

Although App Runner was more suited for my use case, time and effort were already wasted, so I decided to write this article to possibly prevent you (and future me!) from dealing with the same scenario.

This post contains a brief overview of the API, how it was deployed to AWS Elastic Beanstalk, the issues encountered that led to using App Runner, and how it was successfully deployed. It wraps up with a summary of how to choose the better option for your use case.

This AWS article on choosing the right container service is also a good place to start when making this decision. Both App Runner and Elastic Beanstalk are provisioning services, so they handle the orchestration layer complexities and are optimised for ease of use.

Background

The API was built using Express.js and integrated with a MySQL database using Sequelize. This meant I needed to deploy both the database and API, which was not a problem.

First, I created a database on AWS RDS using the MySQL engine, added the credentials to connect to my API, switched to the production configuration, and synced the database.

Next was deploying the API.

AWS Elastic Beanstalk

Elastic Beanstalk is a fully managed service for deploying and managing web applications, with support for containers. I deployed to Beanstalk using the following steps:

Issues Encountered

1) Error connecting to the database

Although the deployment was successful, I kept getting "502 Bad Gateway" errors when accessing any endpoint. I checked the logs and it showed some ETIMEDOUT errors.

Fix: Create a security group with inbound rules for your database (in my case, MySQL) to allow traffic from the Beanstalk environment (via its security group) into the database.

The full solution can be found here.

This error also occurred with App Runner but the resolution required an extra step, as described later below.

2) Mixed Content errors

This was the main limitation that caused the switch.

As you might expect, the project was full-stack and the web app was deployed to Netlify, which deploys to HTTPS but Beanstalk primarily deploys applications to HTTP. This resulted in Mixed Content errors whenever a request was sent to the API from the front-end.

To resolve the issue, a custom domain is needed to configure the Beanstalk environment with HTTPS. However, since I did not want to purchase one, I attempted to use a load balancer, where the balancer terminates the HTTPS connection before getting to the instance and listens for the request on HTTPS with a self-signed certificate.

Resources used for the solution can be found at the bottom of this article.

The fix worked and I was able to get rid of the Mixed Content error. However, this flags the frontend connection as non-secure because the self-signed security certificate on the API is not trusted by Chrome.

It was at this point I decided to explore other services as I had already spent too much time creating a workaround for Beanstalk.

P.S. I also deployed the web app to AWS Amplify but eventually switched to Netlify because, unlike Amplify, it allows editing of the URL of the deployed app without having to use a custom domain.

After doing some research and discovering that App Runner deploys applications to HTTPS, I decided to use it instead.

AWS App Runner

App Runner is a fully managed service for deploying web applications and API services easily by connecting your source code, containerising and then deploying.

It offers the simplest approach to deploying these applications without having to manage the infrastructure.

Extra Fix for Issue Encountered with Database

When creating the service, under "Networking" configurations, configure the service to use "custom VPC" for outgoing traffic and create a new VPC Connector.

Note: Ensure that the security group previously created for database access is included in the security groups for the VPC Connector. It should be configured with the default security group for the custom VPC being used for the service or opened up for all IP addresses (not recommended).

Conclusions

Both services can be used to deploy Node.js APIs and each has its pros and cons.

Linking the article on choosing the right AWS container service again.

1) If you want the simplest approach, App Runner is the choice for you.

2) If you have a custom domain for your API, you can try Elastic Beanstalk. If not, App Runner is a better choice.

3) If your application does not need regular updates, Elastic Beanstalk is still good to go. However, if you regularly make updates to your application, App Runner is a more suited choice.

This is because new updates are deployed on Beanstalk by uploading the updated .zip version of the application and switching. As far as I know (please correct me if I'm wrong), this is how updates are typically made and the process is too manual in my opinion.

On the other hand, App Runner connects to a GitHub repository (or image registry) and can be configured for automatic redeployment when new updates are pushed to the repository.

Summary

Best for AWS Elastic Beanstalk
1) Custom domain available, 2) App is not regularly updated.
Best for AWS App Runner
1) Simplest solution to use without any prior knowledge, 2) no custom domain for API, 3) app is regularly synced with new changes.

Thank you for reading and I hope you find it useful!