A learning project built to explore multi-tenancy concepts in a Spring Boot application. The goal was to understand and implement two different multi-tenancy strategies from scratch.
The application is deployed on AWS using:

- ECS Fargate — runs the Spring Boot containers
- Application Load Balancer — distributes traffic across tasks
- RDS PostgreSQL — multi-tenant database (schema-per-tenant)
- ElastiCache Redis — caching layer for tenant schema lookups
- ECR — Docker image registry
- VPC with public/private subnets across 2 AZs
- NAT Gateway — allows private subnets to reach the internet (for ECR pulls)
All tenants share the same tables. Tenant isolation is achieved by:
- Passing
X-Tenant-Idin every request header - A
TenantFilterthat intercepts each request and extracts the tenant ID - A Hibernate filter (
TenantHibernateFilter) that automatically appends the tenant ID condition to every query via the session factory
Each tenant gets their own isolated PostgreSQL schema. When a new tenant is onboarded:
- A new schema is created dynamically (
CREATE SCHEMA tenant_<company_code>) - Flyway runs the DDL migration scripts against that schema, creating all the tables
- A
MultiTenantConnectionProviderswitches thesearch_pathto the correct tenant schema on every request - A
CurrentTenantIdentifierResolvertells Hibernate which schema to use based on the current request context - The tenant schema is resolved from the JWT token and stored in a
ThreadLocalviaTenantContextThe second approach is what the final implementation uses.
- Java 21
- Spring Boot 3
- Spring Security — JWT authentication with RSA key pairs
- Spring Data JPA / Hibernate — with multi-tenancy support
- PostgreSQL — schema-per-tenant isolation
- Flyway — automated schema migrations per tenant
- HikariCP — connection pooling
- Lombok
- Java 21
- PostgreSQL
- Maven
- Clone the repository
- Create a PostgreSQL database
- Copy
.env.exampleto.envand fill in your database credentials - Generate RSA key pair and place them under
certs/ - Run the application — Flyway will handle the public schema migrations automatically
./mvnw spring-boot:runThe app runs on http://localhost:8080. Swagger UI is available at http://localhost:8080/swagger-ui.html.
The full infrastructure is defined in Terraform under terraform/.
cd terraform
terraform init
terraform applyAfter apply completes, push your Docker image:
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin
docker build -t /saas-app:latest --push .ECS will pick up the image automatically.
Get the ALB URL:
terraform output alb_dns_nameTo tear everything down:
terraform destroyThis project is a learning portfolio piece built by following the "Alibou" SaaS multi-tenancy tutorial, then extending it with full AWS deployment and Terraform automation.