Using Oracle Database with Docker for Spring Boot Integration Testing
Introduction
Testing against the same database you use in production is crucial for catching environment-specific bugs early. However, setting up Oracle locally has traditionally been painful—complex licensing, heavy resource requirements, and tedious configuration.
Enter Oracle Free (formerly Oracle XE) with Docker. Oracle now provides lightweight, free Docker images that make local Oracle development surprisingly easy. Combined with Spring Boot’s Docker Compose support, you can spin up a real Oracle database automatically when your application starts.
This guide shows how to set up Oracle in Docker for integration testing in a Spring Boot application.
The Setup
What We’re Building
┌─────────────────────────────────────────────────────────┐
│ Spring Boot Application │
│ ┌───────────────────────────────────────────────────┐ │
│ │ spring-boot-docker-compose │ │
│ │ (Auto-starts Oracle when app runs) │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Docker Compose │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ Oracle Free 23c (gvenzl/oracle-free) │ │ │
│ │ │ - Lightweight (~500MB vs 8GB+ for full) │ │ │
│ │ │ - Fast startup with "faststart" variant │ │ │
│ │ │ - Persistent volume for data │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Step 1: Docker Compose Configuration
Create docker/compose.yml in your project:
services:
oracle:
image: gvenzl/oracle-free:23-slim-faststart
container_name: demo-oracle
environment:
ORACLE_PASSWORD: OracleAdmin123 # SYS/SYSTEM password
APP_USER: DEMO # Application schema user
APP_USER_PASSWORD: demo_local # Application user password
ports:
- "1521:1521"
volumes:
- oracle-data:/opt/oracle/oradata # Persist data between restarts
healthcheck:
test: ["CMD", "healthcheck.sh"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
start_interval: 1s
volumes:
oracle-data:
name: demo-oracle-data
Understanding the Image Choice
gvenzl/oracle-free:23-slim-faststart - Let’s break this down:
| Tag Component | Meaning |
|---|---|
23 | Oracle 23c Free (latest free version) |
slim | Smaller image (~500MB vs 1.5GB full) - no sample schemas |
faststart | Pre-built database files - boots in seconds, not minutes |
Image variants available:
gvenzl/oracle-free:23 # Full (~1.5GB, slower start)
gvenzl/oracle-free:23-slim # Smaller (~500MB, slower start)
gvenzl/oracle-free:23-faststart # Full + fast (~3GB, instant start)
gvenzl/oracle-free:23-slim-faststart # Smaller + fast (~1.5GB, instant start) ✓ Best choice
Environment Variables
| Variable | Purpose |
|---|---|
ORACLE_PASSWORD | Password for SYS and SYSTEM users |
APP_USER | Automatically creates this user with full privileges |
APP_USER_PASSWORD | Password for the application user |
The APP_USER feature is incredibly useful—Oracle automatically creates the user with CREATE SESSION, RESOURCE, UNLIMITED TABLESPACE privileges. No manual user setup required!
Health Check Configuration
healthcheck:
test: ["CMD", "healthcheck.sh"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s # Wait 30s before first check
start_interval: 1s # Check every 1s during startup
This ensures Docker reports the container as healthy only when Oracle is truly ready to accept connections—crucial for Spring Boot’s Docker Compose integration.
Step 2: Maven Configuration
Add the Spring Boot Docker Compose dependency. We use a Maven profile to make it optional:
<profiles>
<profile>
<id>oracle</id>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</profile>
</profiles>
<!-- Always needed: Oracle JDBC driver and Flyway support -->
<dependencies>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc10</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-oracle</artifactId>
</dependency>
</dependencies>
Why a profile?
- Unit tests use H2 (fast, in-memory)
- Integration tests with Oracle only when needed:
mvn test -Poracle - CI/CD can choose which database to test against
Step 3: Application Properties
For Local Development with Oracle
Create application-oracle.properties:
# Oracle connection via Docker
spring.datasource.url=jdbc:oracle:thin:@//localhost:1521/FREEPDB1
spring.datasource.username=DEMO
spring.datasource.password=demo_local
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
# JPA settings for Oracle
spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
spring.jpa.hibernate.ddl-auto=validate
# Flyway migrations
spring.flyway.locations=classpath:db/migration/common,classpath:db/migration/oracle
spring.flyway.baseline-on-migrate=true
# Docker Compose settings
spring.docker.compose.file=docker/compose.yml
spring.docker.compose.lifecycle-management=start_and_stop
For Unit Tests (H2)
Keep your fast H2 setup for unit tests in application-test.properties:
# H2 in Oracle compatibility mode
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=Oracle
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
Step 4: Flyway Migrations
Organize migrations by database type:
src/main/resources/db/migration/
├── common/ # Runs on all databases
│ └── V1__initialSetup.sql
├── h2/ # H2-specific syntax
│ ├── V2__alterDemoIdSize.sql
│ └── V3__addIndex.sql
└── oracle/ # Oracle-specific syntax
├── V2__alterDemoIdSize.sql
└── V3__addIndex.sql
Example: Same migration, different syntax
h2/V2__alterDemoIdSize.sql:
ALTER TABLE demo_table ALTER COLUMN DEMO_ID SET DATA TYPE VARCHAR2(200);
oracle/V2__alterDemoIdSize.sql:
ALTER TABLE demo_table MODIFY DEMO_ID VARCHAR2(200);
Step 5: Running the Application
Option A: Manual Docker Start
# Start Oracle
cd docker
docker compose up -d
# Wait for healthy status
docker compose ps # Check STATUS shows "healthy"
# Run application
mvn spring-boot:run -Dspring.profiles.active=oracle
Option B: Automatic with Spring Boot Docker Compose (Recommended)
# Just run the app - Spring Boot starts Docker automatically!
mvn spring-boot:run -Poracle -Dspring.profiles.active=oracle
Spring Boot will:
- Detect
docker/compose.yml - Run
docker compose up - Wait for Oracle healthcheck to pass
- Configure datasource properties automatically
- Start your application
- Run
docker compose downon shutdown
Step 6: Integration Test Configuration
For integration tests that need a real Oracle database:
@SpringBootTest
@ActiveProfiles("oracle")
@Testcontainers
class OracleIntegrationTest {
@Container
static OracleContainer oracle =
new OracleContainer("gvenzl/oracle-free:23-slim-faststart")
.withUsername("TEST_USER")
.withPassword("test_password");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", oracle::getJdbcUrl);
registry.add("spring.datasource.username", oracle::getUsername);
registry.add("spring.datasource.password", oracle::getPassword);
}
@Test
void shouldPersistToOracle() {
// Your test here - running against real Oracle!
}
}
Add Testcontainers dependency:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>oracle-free</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
Performance Tips
1. Use faststart Images
The faststart variants include pre-built database files. Startup time comparison:
| Image | First Start | Subsequent Starts |
|---|---|---|
23-slim | ~2-3 minutes | ~30-60 seconds |
23-slim-faststart | ~10-15 seconds | ~5-10 seconds |
2. Persist Data with Volumes
volumes:
- oracle-data:/opt/oracle/oradata
Without volumes, Oracle recreates the database on every container start. With volumes:
- Data persists between restarts
- Subsequent starts are much faster
- Your test data survives container recreation
3. Use H2 for Unit Tests
Reserve Oracle for integration tests. Unit tests should be fast:
// Unit test - uses H2
@DataJpaTest
@ActiveProfiles("test")
class RepositoryTest {
// repository tests here
}
// Integration test - uses Oracle
@SpringBootTest
@ActiveProfiles("oracle")
class FullIntegrationTest {
// full integration tests here
}
4. Connection Pool Tuning
# Reduce pool size for local development
spring.datasource.hikari.maximum-pool-size=5
spring.datasource.hikari.minimum-idle=2
Troubleshooting
Container Won’t Start
# Check logs
docker logs demo-oracle
# Common issues:
# - Port 1521 already in use
# - Insufficient memory (Oracle needs ~1GB RAM)
# - Volume permission issues
Connection Refused
# Verify container is healthy
docker compose ps
# Test connection manually
docker exec -it demo-oracle sqlplus demo/demo_local@//localhost:1521/FREEPDB1
Slow Startup
- Switch to
faststartimage variant - Increase Docker memory allocation
- Use SSD storage for Docker volumes
Conclusion
With Oracle Free Docker images and Spring Boot’s Docker Compose integration, testing against Oracle locally is now straightforward:
- Zero manual setup – Docker Compose handles everything
- Production parity – test against real Oracle locally
- Developer-friendly – fast startup, clean shutdown, CI-ready