❌ The Problem
You’ve got a Spring Boot application using PostgreSQL in Docker. Everything works locally, but in GitLab CI, integration tests fail with:
Connection refused: Cannot connect to PostgreSQL at cookbook-db:5432
🖼️ Example of a failed pipeline
🔍 Root Cause Breakdown
This error typically happens due to one or more of the following:
- PostgreSQL container isn’t ready when the tests run.
- Incorrect container aliasing — Spring Boot can’t resolve
cookbook-db
. - Missing or misconfigured environment variables (e.g., DB user/password).
- Port conflicts or unexposed ports in CI runner.
✅ Working Solution
🔧 1. .gitlab-ci.yml
Configuration
variables:
POSTGRES_USER: youruser
POSTGRES_PASSWORD: yourpassword
POSTGRES_DB: cookbook-db
POSTGRES_HOST_AUTH_METHOD: trust
stages:
- build
- test
maven-build:
stage: build
image: maven:3.9.9-eclipse-temurin-21
script:
- mvn clean compile
artifacts:
paths:
- target/
integration-test:
stage: test
image: maven:3.9.9-eclipse-temurin-21
services:
- name: postgres:15.4-alpine
alias: cookbook-db
command: ["postgres", "-c", "max_connections=100"]
variables:
SPRING_DATASOURCE_URL: jdbc:postgresql://cookbook-db:5432/${POSTGRES_DB}
SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER}
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
script:
- |
echo "Waiting for PostgreSQL..."
for i in {1..10}; do
nc -z cookbook-db 5432 && break
echo "Retrying in 5s... ($i/10)"
sleep 5
done
- mvn verify -Dspring.profiles.active=ci
artifacts:
when: always
reports:
junit: target/surefire-reports/*.xml
🗃️ 2. application-ci.properties
spring.datasource.url=${SPRING_DATASOURCE_URL}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
spring.jpa.hibernate.ddl-auto=validate
spring.sql.init.mode=always
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=5
management.endpoint.health.probes.enabled=true
management.health.datasource.enabled=true
🔍 Debugging Tips
Check Docker Network in CI
Ensure containers are on the same network:
docker network ls
docker inspect <network-name>
🔧 What Fixed It
- Added retry loop with
nc
to wait until the DB is ready. - Aliased the service correctly (
cookbook-db
). - Passed DB credentials via GitLab CI environment variables.
- Increased max connections for concurrent test support.
🚧 Common Pitfalls
Issue | Fix |
---|---|
“Connection Refused” | Add wait loop using nc or wait-for-it |
Schema not initialized | Use spring.sql.init.mode=always or Flyway/Liquibase |
Port conflict | Check CI logs with netstat -tulpn |
🧪 Best Practices
✅ Use Testcontainers (Alternative)
@Testcontainers
class IntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine");
}
✅ Always Pin Versions
services:
- name: postgres:15.4-alpine
✅ Tune Connection Pool
spring.datasource.hikari.maximum-pool-size=5
spring.datasource.hikari.connection-timeout=30000
⚡ Performance Tips
Technique | Before | After |
---|---|---|
Parallel stages | 120s | 85s |
Testcontainers | 90s | 65s |
Cached dependencies | 45s | 12s |
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2/repository
- target/
❓ FAQ
Q: Why use nc -z
instead of just sleep
?
A: nc
checks if the port is actually open. sleep
just delays blindly.
Q: How do I run migrations before tests?
A: Use Flyway in your pipeline:
script:
- mvn flyway:migrate -Dflyway.configFiles=src/main/resources/flyway-ci.conf
- mvn verify
Q: Can I use Testcontainers in GitLab CI?
A: Yes! Enable Docker-in-Docker (DinD):
services:
- docker:28.0.4-dind
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_VERIFY: "1"
🎉 Final Thoughts
By implementing proper service configuration, health checks, and environment isolation, your GitLab CI integration tests will run just as smoothly as they do locally.
✅ PostgreSQL ready before tests
✅ Stable container networking
✅ Reliable Spring Boot DB config
🖼️ Example of a successful pipeline
👉 See working example on GitHub
Let me know if you want a version of this as a blog post or Markdown doc!