How to Run a Postgres Service in a Jenkins Pipeline
Jenkins runs sidecar services with the Docker Pipeline plugin's docker.image().withRun, which starts and links a container around your steps.
Use the Docker Pipeline plugin's sidecar pattern: withRun starts a container, gives you its handle, and tears it down when the block exits.
Postgres sidecar
Start Postgres, wait for it, run tests against it, then it is automatically removed.
pipeline {
agent any
stages {
stage('Test') {
steps {
script {
docker.image('postgres:16').withRun(
'-e POSTGRES_PASSWORD=postgres -p 5432:5432'
) { c ->
sh 'until pg_isready -h localhost -p 5432; do sleep 1; done'
withEnv(['DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres']) {
sh 'npm ci && npm test'
}
}
}
}
}
}
}Gotchas
- Requires the Docker Pipeline plugin and a Docker daemon on the agent.
- Wait for the DB to accept connections (
pg_isreadyloop) before connecting -withRunreturns as soon as the container starts. - If your build runs inside a container, link via the inner network alias instead of
localhost.
Verify it actually works
Validate the pipeline definition against the running controller before committing. Jenkins parses declarative pipelines strictly, and a syntax error surfaces as a failed build rather than a clear parse message.
# validate a Jenkinsfile against the live controller
curl -X POST -F "jenkinsfile=<Jenkinsfile" \
https://your-jenkins/pipeline-model-converter/validate
# replay a build with modified script to test a change without committing
# Build page -> Replay -> edit -> RunAgent and workspace assumptions that break in CI
- An agent label that matches no online agent leaves the build queued indefinitely rather than failing.
- Workspaces are reused between builds by default, so stale files from a previous run can mask or cause failures. Use
cleanWs()or a fresh workspace when correctness matters. - Tools resolved from the controller PATH are not necessarily on the agent PATH. Declare them in a
toolsblock or install them in the pipeline. - Credentials bound with
withCredentialsare masked in logs but still visible to any process you launch; avoid passing them as command-line arguments.