Build Multi-Region Apps on AWS
Deploy your web app to multiple AWS regions with automatic GitHub deployments and instant failover readiness
Introduction
This project has been archived
We're no longer updating this project - we recommend checking out our active projects instead!
If you've already completed this project, don't worry! Your documentation is still available for you to check out and share.
If you have any questions, leave a question in our Discord and we'll get back to you ASAP!
⚡️ 30 second Summary
Your app runs in one AWS region. That region has an outage. Your users get errors. You scramble to manually deploy somewhere else while your phone blows up with alerts. This is what single-region deployment looks like under pressure.
Now imagine your app is already running in two regions. When one goes down, traffic routes to the other automatically. No scrambling, no manual intervention, no 3am firefighting. Your users don't even notice. The dream!
What you'll build
In this project, you'll build this same foundation: a Node.js web app deployed to multiple AWS regions with automatic deployments from GitHub.
We'll be using AWS App Runner to deploy our app in two regions: us-east-1 and us-west-2.
Want a complete demo of how to do this project, from start to finish? Check out our 🎬 walkthrough with Kahu or 🎬 fast track it with Maximus
What experience do I need?
This project doesn't require any prior AWS experience. You'll learn how App Runner works, how to set up automatic deployments, and how to deploy the same app to multiple regions - the foundation for production-grade resilience.
💡 What are the prerequisites?
Just an AWS account - if you don't have one yet, complete Set Up Your AWS Account first (it's free!)
Not sure if this project is right for you? Check if it matches your goals
If you're up for a bit of a challenge, quiz yourself on the key concepts up ahead in this project.
This project is part of a series:
Launch Your App in Virginia (us-east-1)
Time to get your first region live!
In this step, you're going to:
- Set up a GitHub account to store your code
- Create a simple Node.js Express app
- Deploy to AWS App Runner in us-east-1
- Configure a GitHub integration for auto-deployments, so every code push goes live in minutes.
- Verify your app is running with a public HTTPS endpoint
✔️ Yep, I have a GitHub account
You're all set!
Venture onwards brave traveller.
ⓧ Nope, I need to create one
- Head to github.com and click Sign up.
- Sign up with Google, Apple, or fill in the form with your email, password, and username.
- Click Create account and verify your email address.
Great, now you have a GitHub account! Let's fork the starter repo below.
Create a GitHub Repository
- Click your profile picture in the top right corner and select Repositories.
- Click the green New button.
- For Repository name, enter multi-region-app.
- Select Private.
- Click Create repository.
- Copy the HTTPS URL from the Quick setup section.
- Paste your repository URL here: Your Github Repo URL
- We'll use it again shortly!
Clone the Repository in Cursor
Now let's clone this repo to your computer using Cursor, an AI-powered code editor.
- Open a terminal and run cursor --version to check if Cursor is installed.
✔ Aha! I see a version number
- Fantastic! This means you have Cursor already installed.
- Open Cursor.
- Press Cmd+Shift+P (Mac) or Ctrl+Shift+P (Windows) to open the command palette.
- Type Git: Clone and press Enter.
- Paste your repository URL:
[[YOUR_REPO_URL="Your Github Repo URL"]]
- Choose a folder on your computer (like your Desktop) to save the project.
- When prompted, click Open to open the cloned repository.
- Nice! Your project is open and ready to go.
ⓧ Nope, command not found
- No problem - this means you don't have Cursor installed yet. Let's fix that.
- Navigate to cursor.com/downloads and download the installer for your operating system.
- Run the installer and follow the prompts to install Cursor.
- Once installed, open Cursor.
- Press Cmd+Shift+P (Mac) or Ctrl+Shift+P (Windows) to open the command palette.
- Type Git: Clone and press Enter.
- Paste your repository URL:
[[YOUR_REPO_URL="Your Github Repo URL"]]
- Choose a folder on your computer (like your Desktop) to save the project.
- When prompted, click Open to open the cloned repository.
Getting a 'Git not found' error?
That's totally okay! This just means you don't have Git installed on your computer.
- Head over to git-scm.com/downloads.
- Download and run the installer for your operating system.
- Once installed, restart your IDE and try again.
Still having issues? Troubleshoot here or share your error with the NextWork community!
Create Your Express App with AI
Excellent work. We've got our GitHub repo created, and Cursor is waiting for us to build.
Let's use Cursor's AI to create our Node.js Express app. Instead of writing code manually, we'll use prompts!
- Open the Cursor chat panel by pressing Cmd+L (Mac) or Ctrl+L (Windows).
- Copy and paste this prompt:
Create a simple Express.js web server I can deploy to AWS App Runner. I need a package.json with the Express dependency and an index.js that listens on process.env.PORT (or 8080 as fallback). Just a single route at "/" that returns "Hello from AWS!" is fine. Make sure there's a start script so App Runner can run it.
- Press Enter to send the prompt.
- Have a look... Cursor is generating both package.json and index.js files.
- Click Accept to add the files to your project.
Whoa, whoa, whoa. What happened?
Cursor generated two files: package.json (a configuration file for your app that lists dependencies like Express) and index.js (the code for a minimal web page that says "Hello from AWS!").
In the index.js file, you might notice a little line that looks like this: process.env.PORT || 8080. This pattern is important - it means "use a default port number OR use port number 8080".
When we start using App Runner, it automatically provides the port environment variable, so you don't need a .env file. The "8080" is just a fallback for local testing.
Push Your Code to GitHub
Our code is generated, now let's push these new files to your GitHub repository.
- Open the terminal in Cursor: Ctrl+` (Mac) or Ctrl+` (Windows).
- Run these commands:
git add .
git commit -m "Add Express app"
git push
- This saves and pushes your changes to your GitHub repo we created earlier.
- Read the terminal output carefully...
✔ GitHub push succeeded
- When your push succeeds, you'll see lines in your terminal like "100% done".
- If you don't have any errors, it probably succeeded! Best to double check...
ⓧ Authentication error
That's okay - all it means is that you need to login to GitHub and authenticate your account.
GitHub no longer accepts passwords for git operations, which leaves us wtih two options:
Option 1: Use GitHub CLI (Recommended)
- Install GitHub CLI from cli.github.com
- Run gh auth login and follow the prompts
- Try pushing again: git push
Option 2: Use a Personal Access Token
- Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
- Generate a new token with repo scope
- Use this token as your password when prompted
Note
Got stuck? Don't worry about it - there's always help on hand. Jump into the NextWork Community and post your question. We're here to help.
Let's go to your repository and make sure that everything was pushed correctly.
- Enter the following link into a new browser tab:
[[YOUR_REPO_URL="Your Github Repo URL"]]
- Do you see your package.json and index.js files?
- Awesome! Great success setting up your app in GitHub.
Deploy to AWS App Runner
Okay! We have an app, and it's safely stored in GitHub. Now let's deploy your app to AWS App Runner in the us-east-1 region.
- Go to console.aws.amazon.com and sign in.
- Search for "App Runner" in the AWS Console search bar and click AWS App Runner.
What is AWS App Runner?
App Runner does what it says - it runs apps!
In more detail, App Runner is a fully managed container service that runs your web applications without you managing servers. You give it your source code, and it handles building, deploying, scaling, and load balancing automatically.
The "automatic deployment" setting means whenever you push code to GitHub, App Runner detects the change and redeploys your app - no manual intervention needed. This is CI/CD (Continuous Integration/Continuous Deployment) built right in.
- Make sure you're in the us-east-1 region (N. Virginia). Check the region selector in the top right corner.
- Click Create service.
- For Source, select Source code repository.
- For Provider, select GitHub.
- Click Add new to create a GitHub connection.
- For Connection name, enter github-connection-east.
- Click Install another to authorize AWS to access your GitHub repositories.
- In the GitHub popup, select Only select repositories.
- Choose your multi-region-app repository.
- Click Install & Authorize.
- Back in AWS Console, your GitHub account should now appear in the GitHub app dropdown.
- Click Next.
- For Repository, select your multi-region-app repository.
- For Branch, select main.
- For Deployment trigger, select Automatic.
Why would we select Automatic?
Selecting automatic means than any changes we make to our code base on the main branch will trigger a new deployment. Very convenient.
- Click Next.
Phew! Nice work connecting our GitHub repo with App Runner.
Configure Build Settings
Now onto configuring App Runner's Build Settings...
- For Configuration file, select Configure all settings here.
- Set Runtime to Nodejs 18.
- Set Build command to npm install.
- Set Start command to npm start.
- Set Port to 8080.
- Click Next.
What are all these build settings?
- The build command (npm install) downloads all your dependencies, like Express before running your app.
- The start command (npm start) tells App Runner how to launch your application - it runs the start script from your package.json.
- The port setting (8080) tells App Runner which port your app listens on so it can route incoming traffic correctly.
App Runner also automatically provides HTTPS with a valid SSL certificate - no configuration needed.
Configure Service Settings
It's the final stretch! Time for the Service Settings...
- For Service name, enter multi-region-app-east.
- Leave the default settings for CPU, memory, and scaling.
- Click Next.
- Review your settings
- Click Create & deploy.
- Exciting! Wait for the deployment to complete. This takes about 2-3 minutes.
What happens during deployment?
App Runner is doing several things behind the scenes: cloning your repository, running npm install to fetch dependencies, bundling your application, and launching it in a managed container. It also provisions a load balancer, configures auto-scaling, and sets up health checks. All of this infrastructure that would normally take hours to set up manually happens automatically in minutes.
Verify Your Deployment
- Once the status shows Running...
- Find the Default domain URL in your service details.
- Click the URL or copy it into your browser.
- You should see: Hello from AWS!
WOOHOO! Your app is now live!
App Runner automatically adds HTTPS by creating a TLS/SSL certificate automatically. This helps to keep our app secure and safe.
Getting errors?
Check the deployment logs for specific errors:
- Click on your service in App Runner
- Go to Deployments tab
- Click on the failed deployment to see logs
Common issues:
- Build failed: Check that package.json has the correct structure and dependencies
- Start command failed: Verify npm start works locally first
- Port mismatch: Make sure your app listens on port 8080 (check index.js)
- If nothing else works, get in touch at the NextWork Community
Expand to Oregon (us-west-2)
Okay - so far, so good.
We've built an app. Added it to GitHub. And deployed our app to run on the East Coast.
Now let's put it on the West Coast too!
Why is this important?
This is the foundation of multi-region architecture - the same pattern Netflix uses to serve 200+ million users. When us-east-1 goes down (and it has!), us-west-2 keeps serving traffic. That's real redundancy.
You'll follow a similar process to Step 1 - just in a different region with a new GitHub connection.
In this step, you're going to:
- Deploy the same app to AWS App Runner in us-west-2
- Create a new GitHub connection for the second region
- Verify both regions are running simultaneously
Switch to us-west-2 Region
- In the AWS Console, click the region selector in the top right corner.
- Select US West (Oregon) us-west-2.
Why us-west-2?
us-east-1 and us-west-2 are 2,500 miles apart - a disaster in one won't affect the other. Plus, west coast users get ~50ms response time from us-west-2, compare to a ~150ms response time from us-east-1.
Easy as that! We're now operating our AWS console from the us-west-2 region.
Anything we configure or do now, is from the us-west-2 region rather than the east region we started with.
Create App Runner Service in us-west-2
Now that we're in our new region, let's set up our App Runner again. This time in the west!
- Navigate to App Runner and click Create service.
- For Source, select Source code repository.
- For Provider, select GitHub.
- Click Add new to create a GitHub connection for this region.
- For Connection name, enter github-connection-west.
- For GitHub app, select your GitHub account from the dropdown (already installed from Step 1).
- Click Next.
- Select your multi-region-app repository and main branch.
- For Deployment trigger, select Automatic.
- Click Next.
Why do I need another GitHub connection?
AWS resources are region-specific - us-west-2 can't see resources in us-east-1. Each region needs its own connection.
💡 This isn't theoretical
In December 2021, us-east-1 went down for 7+ hours. Netflix, Disney+, Slack, all offline. Companies with multi-region? They were able to switch regions and keep going.
Configure Build Settings (Same as us-east-1)
- For Configuration file, select Configure all settings here.
- Set Runtime to Nodejs 18.
- Set Build command to npm install.
- Set Start command to npm start.
- Set Port to 8080.
- Click Next.
Configure Service Settings
- For Service name, enter multi-region-app-west.
- Leave the default settings.
- Click Next.
- Review your settings and click Create & deploy.
- Grab a cup of tea or a snack, and wait for the deployment to complete!
Verify Both Regions
Once the status shows Running, you have the same app running in two regions! Let's verify both are working.
- In us-west-2, find the Default domain URL and open it in your browser.
- You should see: Hello from AWS!
- Switch back to us-east-1 and verify that service is still running.
Both regions are now serving identical content. Woohoo! But how do you know which region is responding? Let's fix that in the next step.
Is multi-region worth the cost?
We're keeping this project free and low-key but if you were in a large company, you'd be paying for double for infrastructure. Not everything needs this. If you're supporting payment systems, yes, this is important. Internal admin tools? Probably not. Always weigh cost of downtime vs cost of redundancy.
Running into errors?
Check the deployment logs:
- Go to App Runner → Select your service → Deployments tab
- Click on the failed deployment to see logs
Common issues:>
- GitHub connection failed: Each region needs its own connection - make sure you created github-connection-west in us-west-2
- Repository not found: Verify you selected the correct repository and branch
- Build failed: Same issues as Step 1 - check package.json structure
Add Region Detection
Both regions are live, but how do you know which one is responding?
Right now, it's hard to tell which region we're on when viewing our app. Let's make our app display the region that is serving each request.
This is going to be super useful this for verifying failover works correctly and debugging routing issues.
In this step, you're going to:
- Update the app to display the current AWS region
- Push changes and watch both regions auto-deploy
- Verify each region shows its own region name
Display the region on your app
The first question to ask, is how do we actually get the current region our app is in? Where is that stored?
Do you remember at the start of the project, when we talked briefly about environment variables? Turns out, AWS App Runner automatically provides the AWS_REGION environment variable to our app.
What are environment variables?
Environment variables are values that exist outside your code but can be read by your application at runtime. They're perfect for configuration that changes between environments - like which region you're running in, database connection strings, or API keys.
Because AWS has so many hosting services, App Runner sets our AWS_REGION variable automatically. This saves us from having to manually update our app's region everytime it changes.
We're going to us the AWS_REGION variable provided by App Runner and display it in our app. Cursor will help us to implement the change.
- Open the Cursor chat panel by pressing Cmd+L (Mac) or Ctrl+L (Windows).
- Copy and paste this prompt:
Update index.js so it shows which AWS region
is responding.
Read the AWS_REGION environment variable
(use 'local' as a fallback for testing) and
change the response to say "Hello from {region}!"
instead.
Also log the region when the server starts
so I can see it in the console.
- Press Enter to send the prompt.
- Review the changes Cursor proposes.
What did Cursor change?
Cursor added a line to read the AWS_REGION environment variable. No .env file needed since App Runner automatically sets AWS_REGION based on which region your service is running in.
- Update anything that you think is off, or you'd like to see. Be brave!
- Click Accept to apply the changes.
Push Your Changes to GitHub
Remember, we need to push all our changes to GitHub so we can deploy our app through App Runner.
- Save your file.
- Open the terminal in Cursor: Ctrl+` (Mac) or Ctrl+` (Windows).
- Run these commands:
git add .
git commit -m "Add region awareness"
git push
- Because you configured automatic deployments, both App Runner services will detect the change and start deploying.
How does automatic deployment work?
When you push to GitHub, App Runner's GitHub integration detects the new commit and triggers a deployment in both regions automatically. Each region independently pulls your code, builds it, and deploys it.
This is continuous deployment in action - code changes go from your laptop to production in minutes without manual intervention. Both services watch the same main branch, so they always stay in sync.
Wait for Deployments
- Navigate back to your AWS Console
- Click back into App Runner - let's check both App Runner services.
- In us-west-2, your multi-region-app-west service should show a new deployment in progress.
- In us-east-1, your multi-region-app-east service should also show a new deployment.
- Wait 2-3 minutes for both deployments to complete.
Verify Region-Aware Responses
Once both deployments show Running status:
- Open the us-east-1 service URL.
- You should see: Hello from us-east-1!
- Open the us-west-2 service URL.
- You should see: Hello from us-west-2!
✔ Yeehaw! I see it!
Congratulations! Your multi-region deployment is complete!
Now you can clearly see which region is serving each request. This is crucial set up for testing failover scenarios in the next project.
ⓧ Hmmm, I'm having issues
No worries, we'll get it!
Check these steps:
- Did you push your code? Run git status - if you see changes, run git add . && git commit -m "Add region" && git push
- Wait 2-3 minutes for both services to redeploy
- Hard refresh your browser: Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows)
Still showing same region or "local"?
- Check App Runner → Deployments tab to confirm the latest deployment succeeded
- Verify index.js reads process.env.AWS_REGION
Still stuck?
- That's okay! There's always help on hand. Jump into the NextWork Community. We'll be there.
Why does region visibility matter?
Knowing which region serves each request is essential for debugging and verifying your failover setup. In production, you'd typically log the region to your monitoring system rather than display it to users.
But... if we set this up to highlight locaiton, during development and testing, seeing "Hello from us-east-1!" vs "Hello from us-west-2!" instantly tells you which path your traffic took. This becomes critical in the next project when you add automatic failover.
Secret mission
So we can set up our app to be available in multiple regions. But which region do we choose? East or West? Up or down? Close or far?
There's a best practice way to think about this. We call it latency. Regions that have low latency are closer together, and deliver a faster loading time for users.
In this secret mission, you'll add response timing to measure the real performance difference between your regions.
Did you know?
Amazon found every 100ms of latency costs 1% in sales - crazy!
Measure Real-World Latency
Clean Up Your Resources
Clean Up Your Resources
Nice work completing this project!
Choose how much you want to clean up based on whether you're continuing to Project #2.
Stay set up for Project #2
Choose this option if:
- You're continuing to Project #2
- You plan to do Project #2 within the next two weeks
It does cost to keep App Runner going!
Each region costs roughly $5 USD per month to run. If you're trying to keep this entirely free, best to jump into Project #2 immediately or pause services until you're ready.
Steps to keep things running:
- Note your service URLs - you'll need them in Project #2
- Leave both App Runner services as-is
- Continue to Project #2: CloudFront Failover
Pause Services
Choose this option if:
- You want to take a break but plan to continue later
- You're wanting to reduce the risk of being charged by keeping App Runner going.
Steps to pause services:
- Pause us-east-1 service:
- Go to AWS Console → App Runner
- Make sure you're in the us-east-1 region
- Select multi-region-app-east
- Click Actions → Pause
- Pause us-west-2 service:
- Switch to the us-west-2 region in the top right
- Select multi-region-app-west
- Click Actions → Pause
Paused services cost significantly less (~$0.007/hour for provisioned instances vs. active compute). When you're ready to continue, resume the services.
Remove Everything
Choose this option if:
- You're done with this project
- You want to stop all charges
Steps to remove everything:
- Delete us-east-1 service:
- Go to AWS Console → App Runner
- Make sure you're in the us-east-1 region
- Select multi-region-app-east
- Click Actions → Delete
- Type delete to confirm
- Wait for deletion to complete
- Delete us-west-2 service:
- Switch to the us-west-2 region in the top right
- Select multi-region-app-west
- Click Actions → Delete
- Type delete to confirm
- Verify cleanup:
- Check both regions show no App Runner services
- Your GitHub connections and repository can remain (they're free).
Clean up complete!
That's a wrap!
That's a wrap!
You did it! WOOHOO!
What you learned:
- 🚀 How to deploy applications to AWS App Runner in multiple regions
- 🔗 Setting up GitHub integration for continuous deployment
- 🌎 Why each AWS region requires its own GitHub connection
- 🔧 How to make apps region-aware using environment variables
- ⚡ Why multi-region architecture matters for reliability and latency
- 💎 How to measure latency between AWS regions
Ready for the final quiz?
What's next?
You've built the foundation of production-grade infrastructure - a web app running in multiple AWS regions with automatic deployments from GitHub. Next, it's time to put it to the test...
- 🚦 Project #2: CloudFront Failover How do you automatically reroute traffic to a healthy region when one goes down? In Project 2, add automatic failover using CloudFront.
- 🌏 Project #3: Multi-Cloud Deployment with Pulumi What about if AWS goes down completely? Can you automatically run it on GCP instead? In Project 3, build a 3-way failover, managed entirely with Pulumi.
Make sure to join the NextWork Community to connect with other engineers! 🤝
See you there!
p.s. Does it say "Still tasks to complete!" at the bottom of the screen?
This means you still have screenshots left to upload, or questions left to answer!
- Press Ctrl+F (Windows) or Command+F (Mac) on your keyboard.
- Search for the text Return to later.
- Jump straight to your incomplete tasks!
- 🙋♀️ Still stuck? Ask the community!