Spring Boot with AWS App Runner
Learn how to deploy a Spring Boot application on AWS App Runner step by step using Docker, Amazon ECR, environment variables, health checks, logs, auto deployments, and production best practices.
Introduction
In the previous articles, we deployed Spring Boot applications using:
- Amazon EC2
- AWS Elastic Beanstalk
- AWS ECS Fargate
- AWS EKS
In this article, we will learn how to deploy a Spring Boot application on AWS App Runner.
AWS App Runner is a managed service for running web applications and APIs from source code or container images. It can handle infrastructure, load balancing, HTTPS endpoint, scaling, deployment, and logs with less setup compared to ECS or EKS.
Important: AWS documentation currently states that AWS App Runner is no longer open to new customers. Existing customers can continue using the service. This article is useful for existing App Runner users and for learning managed container deployment concepts.
What You Will Learn
- What is AWS App Runner?
- When to use App Runner
- App Runner vs EC2 vs ECS vs EKS
- How to create a Spring Boot REST API
- How to Dockerize Spring Boot
- How to push Docker image to Amazon ECR
- How to deploy ECR image to App Runner
- How to configure environment variables
- How to configure health checks
- How to view logs
- How to deploy new versions
- Common errors and fixes
- Production best practices
What is AWS App Runner?
AWS App Runner is a managed container application service.
It helps developers deploy web applications and APIs without manually managing:
- EC2 servers
- Load balancers
- Auto Scaling groups
- Container orchestration
- SSL endpoint setup
- Deployment pipeline basics
You provide either:
- Source code repository
- Container image from ECR or ECR Public
App Runner creates and runs the service for you.
App Runner Architecture
flowchart TD
DEV[Developer Machine]
APP[Spring Boot Application]
JAR[Build JAR]
IMG[Docker Image]
ECR[Amazon ECR]
APR[AWS App Runner Service]
CW[CloudWatch Logs]
USER[User Browser]
DEV --> APP
APP --> JAR
JAR --> IMG
IMG --> ECR
ECR --> APR
APR --> CW
USER --> APR
Production Style Architecture
flowchart TD
U[Users]
HTTPS[App Runner HTTPS Endpoint]
APP[AWS App Runner Service]
ECR[Amazon ECR]
SM[Secrets Manager]
SSM[SSM Parameter Store]
RDS[(Amazon RDS)]
CW[CloudWatch Logs]
U --> HTTPS
HTTPS --> APP
ECR --> APP
APP --> SM
APP --> SSM
APP --> RDS
APP --> CW
App Runner vs Other AWS Deployment Options
| Service | Best For | Server Management | Container Required |
|---|---|---|---|
| EC2 | Learning VM deployment and full control | Yes | No |
| Elastic Beanstalk | Managed app deployment | Partial | Optional |
| ECS Fargate | Production containers | No | Yes |
| EKS | Kubernetes workloads | Partial | Yes |
| App Runner | Simple managed web apps and APIs | No | Optional |
Important App Runner Concepts
| Concept | Meaning |
|---|---|
| Service | Running App Runner application |
| Source | Code repository or container image |
| ECR | Private container image registry |
| Port | Container listening port |
| Health Check | Endpoint used to verify application health |
| Auto Deployment | Redeploy when new image is pushed |
| Instance Role | IAM role used by app to access AWS resources |
| Access Role | IAM role used by App Runner to pull private ECR image |
Prerequisites
Before starting, you need:
AWS Account
AWS CLI
Docker Desktop
Java 17 or Java 21
Maven
Spring Boot project
Amazon ECR access
App Runner access
IAM permissions
Verify Java:
java -version
Output:
openjdk version "17.0.x"
Verify Maven:
mvn -version
Output:
Apache Maven 3.x.x
Verify Docker:
docker --version
Output:
Docker version 25.x.x
Verify AWS CLI:
aws --version
Output:
aws-cli/2.x.x
Step 1: Create Spring Boot Application
Create a Spring Boot project with:
Spring Web
Spring Boot Actuator
Java 17
Maven
Project structure:
springboot-apprunner-demo
┣ src/main/java/com/codewithvenu/apprunner
┃ ┣ AppRunnerDemoApplication.java
┃ ┗ controller
┃ ┗ HelloController.java
┣ src/main/resources
┃ ┗ application.yml
┣ Dockerfile
┗ pom.xml
Step 2: Create Main Class
package com.codewithvenu.apprunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AppRunnerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(AppRunnerDemoApplication.class, args);
}
}
Step 3: Create REST Controller
Create file:
src/main/java/com/codewithvenu/apprunner/controller/HelloController.java
package com.codewithvenu.apprunner.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.Map;
@RestController
public class HelloController {
@Value("${spring.application.name}")
private String appName;
@Value("${app.environment:local}")
private String environment;
@GetMapping("/")
public Map<String, Object> home() {
return Map.of(
"message", "Spring Boot application is running on AWS App Runner",
"application", appName,
"environment", environment,
"timestamp", LocalDateTime.now().toString()
);
}
@GetMapping("/health")
public Map<String, String> health() {
return Map.of("status", "UP");
}
@GetMapping("/version")
public Map<String, String> version() {
return Map.of("version", "1.0.0");
}
}
Step 4: Configure application.yml
Create file:
src/main/resources/application.yml
server:
port: ${PORT:8080}
spring:
application:
name: springboot-apprunner-demo
app:
environment: ${APP_ENVIRONMENT:local}
management:
endpoints:
web:
exposure:
include: health,info
Important:
App Runner uses PORT as a reserved environment variable. Your application should read it, but you should not manually create an environment variable named PORT in App Runner configuration.
Step 5: Create pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.codewithvenu</groupId>
<artifactId>springboot-apprunner-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-apprunner-demo</name>
<description>Spring Boot deployment on AWS App Runner</description>
<properties>
<java.version>17</java.version>
<spring.boot.version>3.3.0</spring.boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
</plugins>
</build>
</project>
Step 6: Test Application Locally
Run:
mvn spring-boot:run
Input:
curl http://localhost:8080/
Output:
{
"application": "springboot-apprunner-demo",
"environment": "local",
"message": "Spring Boot application is running on AWS App Runner",
"timestamp": "2026-06-26T10:00:00"
}
Health check:
curl http://localhost:8080/health
Output:
{
"status": "UP"
}
Step 7: Build Spring Boot JAR
Input:
mvn clean package
Output:
BUILD SUCCESS
Generated JAR:
target/springboot-apprunner-demo-0.0.1-SNAPSHOT.jar
Step 8: Create Dockerfile
Create file:
Dockerfile
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/springboot-apprunner-demo-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Step 9: Build Docker Image
Input:
docker build -t springboot-apprunner-demo .
Output:
Successfully built image
Successfully tagged springboot-apprunner-demo:latest
Check image:
docker images
Output:
REPOSITORY TAG IMAGE ID SIZE
springboot-apprunner-demo latest abc123xyz 200MB
Step 10: Run Docker Container Locally
Input:
docker run -p 8080:8080 \
-e APP_ENVIRONMENT=docker-local \
springboot-apprunner-demo
Output:
Tomcat started on port 8080
Started AppRunnerDemoApplication
Test:
curl http://localhost:8080/
Output:
{
"application": "springboot-apprunner-demo",
"environment": "docker-local",
"message": "Spring Boot application is running on AWS App Runner",
"timestamp": "2026-06-26T10:15:00"
}
Stop container:
docker ps
docker stop <container-id>
Step 11: Configure AWS CLI
Input:
aws configure
Example input:
AWS Access Key ID: your-access-key
AWS Secret Access Key: your-secret-key
Default region name: us-east-1
Default output format: json
Validate identity:
aws sts get-caller-identity
Output:
{
"UserId": "AIDAEXAMPLE",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/codewithvenu"
}
Step 12: Create Amazon ECR Repository
Input:
aws ecr create-repository \
--repository-name springboot-apprunner-demo \
--image-scanning-configuration scanOnPush=true \
--region us-east-1
Output:
{
"repository": {
"repositoryName": "springboot-apprunner-demo",
"repositoryUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-apprunner-demo"
}
}
Repository URI:
123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-apprunner-demo
Step 13: Login Docker to ECR
Input:
aws ecr get-login-password --region us-east-1 \
| docker login --username AWS \
--password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
Output:
Login Succeeded
Step 14: Tag Docker Image
Input:
docker tag springboot-apprunner-demo:latest \
123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-apprunner-demo:latest
Validate:
docker images
Output:
REPOSITORY TAG
123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-apprunner-demo latest
Step 15: Push Image to ECR
Input:
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-apprunner-demo:latest
Output:
latest: digest: sha256:abc123 size: 1572
ECR Push Flow
flowchart LR
JAR[Spring Boot JAR]
IMG[Docker Image]
TAG[Tag Image]
ECR[Amazon ECR]
JAR --> IMG
IMG --> TAG
TAG --> ECR
Deployment Option 1: Deploy Using AWS Console
Step 16: Open App Runner Console
Go to:
AWS Console
→ App Runner
→ Create service
Step 17: Choose Source
Input:
Source type: Container registry
Provider: Amazon ECR
Repository type: Private
Container image URI: 123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-apprunner-demo:latest
Deployment trigger: Automatic or Manual
Recommended for learning:
Deployment trigger: Manual
Recommended for faster development:
Deployment trigger: Automatic
Step 18: Configure Access Role
Because the image is in private ECR, App Runner needs permission to pull the image.
Input:
ECR access role: Create new service role
AWS can create a role with permissions for ECR access.
Role example:
AppRunnerECRAccessRole
Step 19: Configure Service
Input:
Service name: codewithvenu-apprunner-demo
Virtual CPU: 1 vCPU
Memory: 2 GB
Port: 8080
Environment variable:
APP_ENVIRONMENT=apprunner-dev
Health check:
Protocol: HTTP
Path: /health
Interval: 10 seconds
Timeout: 5 seconds
Healthy threshold: 1
Unhealthy threshold: 5
Step 20: Create and Deploy
Click:
Create & deploy
Output:
Service status: Operation in progress
Service status: Running
App Runner provides a default HTTPS URL:
https://example-id.us-east-1.awsapprunner.com
Step 21: Test App Runner URL
Input:
curl https://example-id.us-east-1.awsapprunner.com/
Output:
{
"application": "springboot-apprunner-demo",
"environment": "apprunner-dev",
"message": "Spring Boot application is running on AWS App Runner",
"timestamp": "2026-06-26T10:45:00"
}
Health check:
curl https://example-id.us-east-1.awsapprunner.com/health
Output:
{
"status": "UP"
}
Version check:
curl https://example-id.us-east-1.awsapprunner.com/version
Output:
{
"version": "1.0.0"
}
Deployment Option 2: Deploy Using AWS CLI
Step 22: Create App Runner ECR Access Role
Create trust policy file:
cat > apprunner-trust-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "build.apprunner.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
Create role:
aws iam create-role \
--role-name AppRunnerECRAccessRole \
--assume-role-policy-document file://apprunner-trust-policy.json
Attach policy:
aws iam attach-role-policy \
--role-name AppRunnerECRAccessRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess
Get role ARN:
aws iam get-role \
--role-name AppRunnerECRAccessRole \
--query 'Role.Arn' \
--output text
Output:
arn:aws:iam::123456789012:role/AppRunnerECRAccessRole
Step 23: Create App Runner Service
Input:
aws apprunner create-service \
--service-name codewithvenu-apprunner-demo \
--source-configuration '{
"AuthenticationConfiguration": {
"AccessRoleArn": "arn:aws:iam::123456789012:role/AppRunnerECRAccessRole"
},
"AutoDeploymentsEnabled": true,
"ImageRepository": {
"ImageIdentifier": "123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-apprunner-demo:latest",
"ImageRepositoryType": "ECR",
"ImageConfiguration": {
"Port": "8080",
"RuntimeEnvironmentVariables": {
"APP_ENVIRONMENT": "apprunner-dev"
}
}
}
}' \
--health-check-configuration '{
"Protocol": "HTTP",
"Path": "/health",
"Interval": 10,
"Timeout": 5,
"HealthyThreshold": 1,
"UnhealthyThreshold": 5
}' \
--instance-configuration '{
"Cpu": "1024",
"Memory": "2048"
}'
Output:
{
"Service": {
"ServiceName": "codewithvenu-apprunner-demo",
"Status": "OPERATION_IN_PROGRESS",
"ServiceUrl": "example-id.us-east-1.awsapprunner.com"
}
}
Step 24: Check Service Status
Input:
aws apprunner list-services
Output:
{
"ServiceSummaryList": [
{
"ServiceName": "codewithvenu-apprunner-demo",
"Status": "RUNNING",
"ServiceUrl": "example-id.us-east-1.awsapprunner.com"
}
]
}
Step 25: Test CLI Created Service
Input:
curl https://example-id.us-east-1.awsapprunner.com/
Output:
{
"application": "springboot-apprunner-demo",
"environment": "apprunner-dev",
"message": "Spring Boot application is running on AWS App Runner",
"timestamp": "2026-06-26T11:00:00"
}
App Runner Deployment Flow
flowchart LR
BUILD[Build Docker Image]
PUSH[Push to ECR]
ROLE[Create ECR Access Role]
SERVICE[Create App Runner Service]
URL[Test HTTPS URL]
BUILD --> PUSH
PUSH --> ROLE
ROLE --> SERVICE
SERVICE --> URL
Step 26: Deploy New Version
Update controller:
@GetMapping("/version")
public Map<String, String> version() {
return Map.of("version", "1.0.1");
}
Build JAR:
mvn clean package
Build Docker image:
docker build -t springboot-apprunner-demo .
Tag image:
docker tag springboot-apprunner-demo:latest \
123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-apprunner-demo:latest
Push image:
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-apprunner-demo:latest
If automatic deployment is enabled, App Runner redeploys automatically.
If manual deployment is enabled, start deployment from Console or CLI:
aws apprunner start-deployment \
--service-arn arn:aws:apprunner:us-east-1:123456789012:service/codewithvenu-apprunner-demo/service-id
Test:
curl https://example-id.us-east-1.awsapprunner.com/version
Output:
{
"version": "1.0.1"
}
Step 27: Environment Variables
App Runner supports environment variables for application configuration.
Examples:
APP_ENVIRONMENT=prod
SPRING_PROFILES_ACTIVE=prod
LOG_LEVEL=INFO
Spring Boot usage:
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
app:
environment: ${APP_ENVIRONMENT:local}
Do not store sensitive values directly as plain text environment variables.
For secrets, use:
- AWS Secrets Manager
- AWS Systems Manager Parameter Store
App Runner can reference sensitive data stored in Secrets Manager and SSM Parameter Store as environment variables.
Step 28: Logs and Monitoring
App Runner sends logs and metrics to CloudWatch.
Console path:
AWS Console
→ App Runner
→ Service
→ Logs
CloudWatch path:
CloudWatch
→ Log groups
→ App Runner logs
Common logs:
Application logs
Deployment logs
Service events
Step 29: Custom Domain
App Runner provides default domain:
https://example-id.us-east-1.awsapprunner.com
For production, configure custom domain:
api.codewithvenu.com
Flow:
flowchart LR
DOMAIN[Custom Domain]
DNS[DNS Provider or Route 53]
APR[App Runner Service]
APP[Spring Boot API]
DOMAIN --> DNS
DNS --> APR
APR --> APP
Step 30: Cleanup Resources
To avoid charges, delete resources after learning.
Delete App Runner service:
aws apprunner delete-service \
--service-arn arn:aws:apprunner:us-east-1:123456789012:service/codewithvenu-apprunner-demo/service-id
Delete ECR repository:
aws ecr delete-repository \
--repository-name springboot-apprunner-demo \
--force \
--region us-east-1
Delete IAM role policy:
aws iam detach-role-policy \
--role-name AppRunnerECRAccessRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess
Delete IAM role:
aws iam delete-role \
--role-name AppRunnerECRAccessRole
Common Errors and Fixes
Error 1: App Runner Cannot Pull ECR Image
Reason:
Missing access role
Wrong ECR URI
ECR repository in wrong region
Image tag does not exist
Fix:
Use AppRunnerECRAccessRole
Verify image URI
Verify image tag
Verify region
Error 2: Service Fails Health Check
Reason:
Wrong health check path
Application failed to start
Wrong container port
Spring Boot not listening on expected port
Fix:
Use /health endpoint
Set container port to 8080
Use server.port=${PORT:8080}
Check CloudWatch logs
Error 3: 500 Error After Deployment
Reason:
Missing environment variable
Database connection failure
Secrets not configured
Application exception
Fix:
Check App Runner logs
Check environment variables
Check Secrets Manager or Parameter Store permissions
Error 4: Deployment Not Updating
Reason:
Auto deployment disabled
Image tag not changed
Image push failed
Manual deployment not started
Fix:
Enable automatic deployment
Use versioned image tags
Run start-deployment
Check ECR image push output
Best Practices
- Use Docker image deployment from ECR
- Use versioned image tags like
1.0.1 - Avoid using only
latestin production - Use health check endpoint
/health - Use environment variables for non-sensitive config
- Use Secrets Manager or SSM Parameter Store for secrets
- Enable image scanning in ECR
- Use CloudWatch logs
- Configure custom domain for production
- Keep Docker image small
- Avoid hardcoding AWS access keys
- Use IAM roles
- Configure proper CPU and memory
- Delete unused services to avoid cost
App Runner vs ECS Fargate
| Feature | App Runner | ECS Fargate |
|---|---|---|
| Setup complexity | Low | Medium |
| Load balancer management | Managed | You configure |
| Container support | Yes | Yes |
| Networking control | Lower | Higher |
| Scaling control | Simple | Advanced |
| Best for | Simple APIs and web apps | Production microservices |
App Runner vs Elastic Beanstalk
| Feature | App Runner | Elastic Beanstalk |
|---|---|---|
| Deployment style | Container or code | App platform |
| Infrastructure visibility | Lower | Higher |
| Simplicity | High | Medium |
| Containers | First-class | Supported |
| Best for | Simple managed container apps | Traditional app deployments |
Interview Questions
What is AWS App Runner?
AWS App Runner is a managed AWS service for running web applications and APIs from source code or container images without managing servers.
What sources can App Runner use?
App Runner can use source code repositories or container images from Amazon ECR and Amazon ECR Public.
Does App Runner manage load balancing?
Yes. App Runner provides a managed endpoint and handles load balancing and scaling for the service.
What is an App Runner access role?
An access role allows App Runner to pull private container images from Amazon ECR.
How do you configure secrets?
Use AWS Secrets Manager or AWS Systems Manager Parameter Store and reference them as environment variables.
What is the common port configuration?
For Spring Boot, use:
server:
port: ${PORT:8080}
Then configure App Runner container port as 8080.
Summary
In this article, we deployed a Spring Boot application on AWS App Runner.
We covered:
- Spring Boot REST API creation
- Dockerfile creation
- Local Docker testing
- Amazon ECR repository
- Docker image push
- App Runner deployment using console
- App Runner deployment using CLI
- Environment variables
- Health checks
- Logs
- New version deployment
- Custom domain concept
- Cleanup
- Best practices
App Runner is useful when you want to deploy web APIs quickly without managing EC2, ECS cluster details, or Kubernetes complexity.
Comments
Share a question, correction, or practical insight about this article.
Checking login status...
Loading approved comments...