Skip to main content

Full-stack preview environments with DBLab 4.0: isolated databases for every pull request

· 7 min read
Bogdan Tsechoev

Preview environments with DBLab 4.0: Isolated databases for every pull request

Preview environments are temporary deployment environments created for each pull request, offering major advantages over traditional shared staging environments. While platforms like Vercel (paid) and Coolify (open-source) solve application deployment, the database remains the bottleneck. Teams typically compromise: sharing one database (causing conflicts), deploying small test databases (lacking realistic data), or cloning large production databases (taking hours and costing heavily). DBLab 4.0's database branching solves this with O(1) economics, spinning up isolated production-scale Postgres clones in seconds for cost-effective full-stack previews.

Why preview environments?

Modern DevOps teams embrace preview or ephemeral environments because of their clear benefits:

  • Safe, isolated testing: Isolated app and database copies allow destructive tests and migrations without impacting others or production.
  • Faster feedback & collaboration: Preview URLs enable stakeholders to see changes live and catch issues early, including data-related changes in context.
  • Per-PR environments = more confidence: Each PR gets its own environment and database for isolated testing and 100% migration validation in CI/CD.
  • Resource efficiency: Ephemeral environments exist only when needed, avoiding shared dev databases and reducing costs—but require fast provisioning without massive storage overhead.

The evolution of preview environments

Preview environments have evolved through four generations:

1st generation: Single staging environment shared by all developers - the bottleneck approach of yesterday that causes conflicts and blocks teams.

2nd generation: Application-only preview environments (Vercel, Coolify) solve deployment isolation but leave databases shared, creating a critical testing gap.

3rd generation: Small isolated databases per environment improve testing but lack realistic production data volumes, missing performance and edge cases.

4th generation: Full production-scale databases with O(1) economics - the ideal combination of complete fidelity (full code + large database) with constant cost and provisioning time regardless of scale.

In an ideal development world, we need two critical characteristics: complete fidelity (full application code + production-scale database) and O(1) economics (constant cost and provisioning time regardless of scale). DBLab 4.0 delivers both.

DBLab 4.0's secret sauce: instant database branching

DBLab Engine enables "git-like" database branching and lighting-fast thin cloning for Postgres. Version 4.0 introduces true database branches - named pointers to data snapshots, just like Git branches. You can maintain a main database branch and create new branches for each feature or PR on demand.

How does this enable preview environments? When a developer opens a pull request, DBLab quickly branches the main database and creates an isolated clone for that PR. The clone is a full Postgres instance that can be modified freely without affecting other branches. Copy-on-write technology provisions clones in seconds regardless of database size, using minimal storage (only deltas). A 1 TiB database clones in ~10 seconds. Multiple clones scale in O(1) time and storage—teams report ~90% cost savings versus separate cloud instances.

DBLab 4.0's database branching treats database state like Git branches: fast to create, easy to delete, and inexpensive to maintain. This removes the database bottleneck for preview environments. Every PR can include a realistic database clone, enabling true end-to-end testing against production-like data, not just mocks.

Benefits of isolated DB previews

Isolated database previews deliver three key advantages. Full-fidelity testing validates schema changes and performance on real production data during the PR stage, eliminating guesswork about migration success. No shared conflicts means every developer gets their own isolated database for each feature, ending traditional dev/staging database conflicts and "works for me" scenarios.

Operational and cost efficiency is remarkable - database cloning takes seconds regardless of size, environments spin up and tear down instantly, and seamless CI/CD integration automates the entire lifecycle via APIs and webhooks. Most importantly, dozens of ephemeral databases won't blow budgets since clones share underlying data (10 databases on 1 TiB = ~1 TiB total). Teams report ~90% cost savings versus separate instances.

How to implement a DBLab-powered preview environment

Setting up isolated database previews with DBLab typically involves your source control and CI/CD pipeline orchestrating the clone lifecycle. Here's a high-level overview of how it can work:

  1. Pull request event - create DB branch and clone: When a new PR is opened (or updated), the CI pipeline calls DBLab to create a new database branch and clone from the latest main branch (or a specific snapshot). This yields a new Postgres instance (clone) dedicated to that PR. It's usually given a unique identifier, e.g. based on the PR number.
  2. Deploy the preview app: Your pipeline or platform (such as Coolify, Kubernetes, etc.) deploys a preview instance of your application. Crucially, it will configure the app to use the new database clone. This typically means injecting the clone's connection credentials (host, port, DB name, user/password) into the app's environment variables (e.g. DATABASE_URL). The app can then start up connected to the isolated DB.
  3. Test and iterate: Now the PR has a live environment (app + DB) available at a unique URL (for example, https://pr-123.preview.example.com). Team members can review the feature and testers can run any checks, even destructive ones, since this DB is a safe sandbox. If the PR pushes new commits, steps 1-2 repeat to update the environment (you might reset or recreate the DB clone to reflect new migrations).
  4. PR merged/closed - cleanup: When the PR is closed or merged, the preview environment is torn down. The CI pipeline will call DBLab to delete the clone and its branch, freeing up resources. The preview application instance is stopped as well. Everything associated with that PR's environment is cleaned up automatically.

Let's look at a code snippet illustrating steps 1 and 2 - how you can automate DBLab actions in a GitHub Actions workflow to create a database clone for a preview:

- name: Create DBLab Database Clone for PR
if: github.event.action != 'closed'
env:
DBLAB_API: ${{ secrets.DBLAB_API }} # Base URL of the DBLab API
DBLAB_TOKEN: ${{ secrets.DBLAB_TOKEN }} # Auth token for DBLab API
DBLAB_DB_USERNAME: ${{ secrets.DBLAB_DB_USERNAME }} # e.g. "postgres"
DBLAB_DB_PASSWORD: ${{ secrets.DBLAB_DB_PASSWORD }} # clone DB user password
DBLAB_DB_NAME: ${{ secrets.DBLAB_DB_NAME }} # e.g. "postgres"
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
echo "Creating DBLab branch and clone for PR-$PR_NUMBER"
BRANCH_NAME="pr-${PR_NUMBER}"
CLONE_ID="pr_${PR_NUMBER}_clone"

# 1. Create a new database branch from 'main'
curl -s -X POST "$DBLAB_API/branch" \
-H "Verification-Token: $DBLAB_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"branchName\": \"$BRANCH_NAME\", \"baseBranch\": \"main\"}"

# 2. Create a new thin clone from that branch
curl -s -X POST "$DBLAB_API/clone" \
-H "Verification-Token: $DBLAB_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"id\": \"$CLONE_ID\",
\"branch\": \"$BRANCH_NAME\",
\"db\": {
\"username\": \"$DBLAB_DB_USERNAME\",
\"password\": \"$DBLAB_DB_PASSWORD\"
}
}"

# Wait for the clone to be ready and retrieve its connection info
for i in {1..30}; do
STATUS=$(curl -s -H "Verification-Token: $DBLAB_TOKEN" "$DBLAB_API/clone/$CLONE_ID" | jq -r '.status.code')
if [ "$STATUS" = "OK" ]; then break; fi
echo "Waiting for DB clone... ($i)"
sleep 5
done
DB_INFO=$(curl -s -H "Verification-Token: $DBLAB_TOKEN" "$DBLAB_API/clone/$CLONE_ID")
DB_HOST=$(echo "$DB_INFO" | jq -r '.db.host')
DB_PORT=$(echo "$DB_INFO" | jq -r '.db.port')
echo "DB clone ready at host=$DB_HOST port=$DB_PORT"

# The application can now use $DB_HOST:$DB_PORT with the provided username/password.
# (Next steps: pass these DB credentials/URL to your preview app deployment)

The script creates a DBLab branch named pr-$PR_NUMBER from the main branch, then creates a clone with unique ID (e.g. pr_123_clone). After waiting for the clone to be ready, it fetches the connection details. The final step is injecting these credentials into the preview application (e.g. as DATABASE_URL environment variable).

For a complete step-by-step implementation, check out our detailed how-to guide that walks through setting up preview environments with DBLab, Coolify, and GitHub Actions.

Note: You can use any CI/CD system. Remember to clean up by deleting clones and branches when PRs close to avoid orphaned resources.

Conclusion

Preview environments have evolved from shared staging bottlenecks to application-only solutions like Vercel and Coolify, but the database remained the missing piece. DBLab 4.0 represents the 4th generation breakthrough: combining complete fidelity (full application + production-scale database) with O(1) economics (constant cost and time regardless of scale).

This evolution transforms development workflows - teams get production-like testing for every PR without the traditional trade-offs between cost, speed, and data realism. DBLab 4.0 doesn't just add databases to preview environments; it completes the vision of truly isolated, full-stack testing that scales economically, making powerful preview environments accessible to every team.