Databases for CI/CD
A fresh database for every test run
Set one secret, and your pipeline can spin up a real, isolated database, seed it, run tests against it, and throw it away. Give it a TTL and it cleans up after itself even when the job crashes.
name: test
on: pull_request
jobs:
test:
runs-on: ubuntu-latest
env:
LAYERBASE_API_KEY: ${{ secrets.LAYERBASE_API_KEY }}
steps:
- uses: actions/checkout@v4
- run: npm i -g layerbase
# Create a throwaway Postgres that self-destructs after 2h,
# even if this job crashes. --json prints the connection string.
- id: db
run: |
URL=$(layerbase cloud create ci-$GITHUB_RUN_ID \
--engine postgresql --ttl 2h --json | jq -r .connectionString)
echo "url=$URL" >> "$GITHUB_OUTPUT"
- run: psql "${{ steps.db.outputs.url }}" -f seed.sql
- run: npm test
env:
DATABASE_URL: ${{ steps.db.outputs.url }}
# Explicit teardown; the TTL is only the safety net.
- if: always()
run: layerbase cloud delete ci-$GITHUB_RUN_ID --yesThe whole loop: create with a TTL, seed, test, delete. The only thing you configure is the LAYERBASE_API_KEY secret.
Why it holds up
Built so a broken run cannot leave a mess
The failure mode with per-run databases is the run that never cleans up. TTLs make that impossible.
One secret, no browser
Create a personal API key in the dashboard, save it as LAYERBASE_API_KEY in your repo secrets, and the CLI talks straight to the cloud API. No OAuth loop, no token to refresh, no login step in the pipeline.
Transient by design
Pass --ttl and the database auto-destroys when the timer runs out, capped at 72 hours. A killed job cannot strand a database against your quota, because expiry is enforced on our side, not by your teardown step.
Clean teardown
Delete it explicitly at the end of the run for an instant reclaim, and let the TTL cover the runs that never reach their teardown step. Either way you are not paying for CI databases you finished with.
Transient databases
A timer, not a cleanup script you hope runs
Add --ttl 2h to a create and the database carries an expiry. When the clock runs out it is destroyed, capped at 72 hours. That guarantee lives on our side, so it holds even if your workflow is cancelled, times out, or a runner dies mid-job.
While a transient database is alive it uses a database slot on your plan, exactly like a normal database, and frees it the moment it expires or you delete it.
export LAYERBASE_API_KEY=sk_...
# Create it. --json prints the connection string.
layerbase cloud create pr-1234 \
--engine postgresql --ttl 2h --json
# ... seed, run tests against it ...
# Delete now, or let the TTL reap it if the job dies.
layerbase cloud delete pr-1234 --yesThe other primitive
Or branch a seeded parent per pull request
When you want production-shaped data in the test database without re-seeding every run, branch instead of create.
Instant seed, no import
A branch is a copy-on-write clone of a parent you already seeded, ready in seconds with its own connection string. The test run gets real data without waiting on a dump and restore. Reset it between runs, delete it when the PR merges.
How branching worksWired into GitHub and Vercel
Connect the integration and every pull request or preview deploy can get its own branch, reset on merge. One note: transient databases cannot be branched, so branch from a persistent seeded parent, not from a throwaway.
GitHub integrationStraight talk on the limits
Creating databases from an API key is metered per calendar month by plan, so shared infrastructure stays healthy. Creates from the dashboard are never metered. The Free plan covers a handful of programmatic creates a month, paid plans cover far more, and dedicated servers are unmetered because the hardware is yours.
Go over the monthly limit and the API returns a clear error with your usage and an upgrade hint, so a pipeline degrades gracefully instead of failing in a confusing way. Reuse a database or a branch to stay under it. Prefer branch-per-PR for busy repos: branch creates are not counted the same way.
If you outgrow the free ceiling, Pro at $15/mo raises the limits and adds always-on databases for the parts of your stack that should never sleep. The exact per-plan numbers live in the API docs so they are always current.
See the API reference and current limitsQuestions
CI/CD database questions
How do I authenticate CI without a browser login?+
Create a personal API key at cloud.layerbase.com/cloud/settings, store it as a repository secret named LAYERBASE_API_KEY, and the layerbase CLI (and the REST API directly) authenticate with it headlessly. There is no OAuth loop and no session token that expires mid-pipeline.
What happens to a transient database if my CI job crashes?+
Nothing stranded. A transient database created with a TTL self-destructs when the timer expires, capped at 72 hours, whether or not your teardown step ever runs. The TTL is enforced by Layerbase, so a crashed or cancelled job cannot leave a database sitting against your quota.
Does a transient database count against my plan?+
While it is alive, yes: a transient database uses a database slot on your plan like any other. It frees the slot the moment it is deleted or expires. Programmatic creates through an API key are also metered per month by plan; see the API docs for the current per-plan numbers.
Should I use a transient database or a branch per PR?+
Use a branch when you want production-shaped seed data instantly and the engine supports branching: fork a seeded parent, reset between runs, delete on the merge. Use a transient database when you want a blank, isolated instance per run with a hard expiry. Transient databases cannot be branched, so branch from a persistent seeded parent instead.
Put a real database in your pipeline
Create a database, mint an API key, and wire up the workflow above. The free tier is enough to run the whole loop before you pay anything.