Back to Article List

How to set up GitHub PR previews in Coolify

How to set up GitHub PR previews in Coolify - How to set up GitHub PR previews in Coolify

Vercel built a culture where every pull request gets its own preview URL automatically and once you've worked that way it's hard to go back. Designers can poke at a feature before it merges. Reviewers can click through the actual UI changes instead of imagining them from a diff. QA can run their checklist on the real built artifact. The cost of opening a PR drops because there's a place to actually use the thing.

Coolify supports the same workflow, it's just slightly more setup than ticking a box. You wire up a GitHub App, set up wildcard DNS, enable preview deployments on the application and decide what the preview environment looks like (shared database with production, separate database, scoped env vars). Done right, every PR opened against your main branch spins up a fresh deploy at pr-NUMBER.preview.yourdomain.com with a comment from Coolify on the PR.

This guide walks through the full setup. Written against Coolify v4.0.0.

What you actually need to make this work

Three pieces. A GitHub App connected as a Source in Coolify (the default GitHub OAuth integration doesn't support previews, you specifically need the App). The Coolify GitHub setup guide walks through the App creation and permissions in detail. A wildcard DNS record pointing at your VPS so each PR gets its own subdomain. The Preview Deployments toggle enabled on your application with the wildcard pattern configured.

Plus some thinking about what each preview should look like, because "spin up a full copy of production for every PR" is rarely what you want once you do the math on database state and resource cost.

Step 1, set up the GitHub App as a Source

If you've already connected a GitHub App as a Coolify Source for your application, skip ahead. If you've been using a personal access token, you need to switch, PATs don't get the webhook events that drive PR previews.

From the Coolify dashboard click Sources in the left sidebar, then Add, then pick GitHub App. Coolify walks you through creating a new GitHub App on your GitHub account or organization. Name it something descriptive (coolify-prod-previews), Coolify pre-fills the permissions and webhook URL.

The permissions Coolify asks for and why each matters for previews specifically:

  • Read access to code, used to clone the repository
  • Read access to metadata, required for any GitHub App
  • Read and write access to pull requests, used to read PR events and post comments with the preview URL
  • Read and write access to checks, used to post commit statuses (build success/failure) on the PR
  • Read and write access to deployments, used to surface deployment status in GitHub's UI
  • Read access to issues, needed because PR comments are technically issue comments

Authorize the app on the GitHub account or organization that owns your repo, then install it on the specific repos you want preview deployments for (you can install it on all repos or selected ones, "selected" is safer).

Back in Coolify, the Source entry should now show as connected with the GitHub App icon. From this point on, every project and application on this Coolify instance can use this Source.

Step 2, set up wildcard DNS

This is the part that surprises people. Each PR preview gets a unique subdomain, which means you need DNS to resolve any subdomain under a given pattern to your VPS. There are two clean ways to do this.

Wildcard A record at your DNS provider

Most DNS providers support wildcard records. The setup is one record:

*.preview.yourdomain.com    A    YOUR_VPS_IP    300

This means any subdomain of preview.yourdomain.com resolves to your VPS. pr-1.preview.yourdomain.com, pr-2.preview.yourdomain.com, all of them.

Cloudflare, AWS Route 53, Namecheap, Google Domains and most other major providers support this. The exact UI varies but the field name is usually "name" or "host" with a value of *.preview (the part before yourdomain.com is the variable). Set the TTL to 300 seconds or shorter.

Verify the record propagated by running dig pr-anything.preview.yourdomain.com from your laptop. If it returns your VPS IP, you're set.

Cloudflare proxied wildcard (alternative)

If your domain is behind Cloudflare, you can do the wildcard with Cloudflare proxying enabled. The setup is the same A record but with the orange cloud (proxied) status. Coolify's Let's Encrypt cert issuance works with Cloudflare in proxied mode if you switch the SSL/TLS mode to Full (strict) after the cert is first issued. The simpler path is gray cloud (DNS only) for the wildcard during initial setup, then turn on proxy after.

What about specific TLDs that don't allow wildcards

A handful of country code TLDs don't allow wildcard DNS records (some .au registrars, occasionally .de resellers). If yours is one of them, you have two options. Move the wildcard to a subdomain you control fully (point pr-N.preview.yourapp.com at your VPS even though your main domain is yourapp.de). Or use Cloudflare to proxy a wildcard at the apex level (Cloudflare's wildcard support at apex is more permissive than most registrar nameservers).

Step 3, enable preview deployments on your application

Open your application in Coolify, find the section labeled Preview Deployments (it's in the application settings, sometimes under a Settings → Preview sub-tab depending on UI version). Toggle it on.

Configure:

  • Domain pattern: this is what URL each PR gets. The standard pattern is pr-{pr_number}.preview.yourdomain.com. The {pr_number} placeholder gets replaced with the actual PR number when each preview spins up
  • Auto-deploy on PR: enable this so opening a PR automatically triggers a preview build. If you'd rather trigger manually (with a comment like /preview or a label), disable auto-deploy and trigger from the PR comments later
  • Auto-stop on PR close: enable this so closing or merging a PR tears down the preview environment automatically. Without this you'll accumulate preview deploys forever and run out of disk

Save. From now on, opening a new PR against the production branch will trigger a preview deploy. Coolify clones the PR's branch, runs the build, starts a container with a Traefik route at pr-NUMBER.preview.yourdomain.com and posts a comment on the PR with the URL.

Step 4, scope environment variables to previews vs production

Preview deployments shouldn't talk to your production database. They probably shouldn't fire production analytics events, send real emails or charge real Stripe customers either. Coolify lets you scope env vars per environment.

In your application's Environment Variables tab, each variable can be marked as production-only, preview-only or both. The pattern that works for most apps:

Shared between production and preview (no scope change needed):

  • Static configuration that doesn't differ between environments
  • Public API keys for services that have separate test vs live modes (Stripe publishable test key, etc.)

Production only:

  • DATABASE_URL pointing at the production Postgres resource
  • Live payment processor keys (STRIPE_SECRET_KEY=sk_live_...)
  • Production OAuth client IDs and secrets
  • Real third-party webhook URLs
  • Production email service keys (so previews don't send real emails)

Preview only:

  • DATABASE_URL pointing at a separate preview database
  • Test mode payment keys (STRIPE_SECRET_KEY=sk_test_...)
  • Email "catch-all" service keys (Mailtrap, Mailpit, etc.) so emails go to a fake inbox
  • Feature flags that should default to "all on" for testing

You set the scope by clicking the variable's edit icon and picking from the environment dropdown. The UI labels are usually "Production", "Preview" or "Both."

Step 5, set up a separate database for previews (recommended)

Sharing the production database with previews works for read-only changes but breaks badly the moment a PR includes a destructive migration. The PR's preview deploy runs the migration against production, you merge it, the migration runs again and fails because the schema already changed. The cleaner setup is a separate Postgres for previews.

Inside your project, click Add a new resource → Database → PostgreSQL. Name it postgres-preview. This is a small Postgres that previews share, you don't get one per PR.

To populate it with a recent snapshot of production data, restore from your most recent production backup (the Coolify database backups guide walks through the dump and restore mechanics). Refresh this dump weekly or monthly so previews work against realistic data.

Set DATABASE_URL_PREVIEW as a preview-only environment variable pointing at this database. In your application code, read the right URL based on if you're in a preview or production context. SvelteKit does this through $env/dynamic/private, Next.js does it through process.env.COOLIFY_PREVIEW (Coolify sets a flag on preview deployments), Laravel reads from env('DATABASE_URL_PREVIEW').

Step 6, set per-preview resource limits

One PR with a runaway build can OOM your VPS and take down your other previews and your production app along with it. Set resource limits per preview so a misbehaving PR can't hurt anything else.

In the application's settings, find the Resource Limits or Container Limits section. Set:

  • Memory limit: 512 MB for static or lightweight apps, 1 GB for typical SvelteKit/Next.js apps, 2 GB for heavier apps with large dependencies
  • CPU limit: 0.5 to 1 CPU is usually enough for previews. Don't share the full CPU with previews if your production app needs predictable performance

These limits apply per preview deployment. If you have 5 PRs open simultaneously, that's 5 × the limit you set. Multiply through and make sure your VPS has the headroom.

For VPS sizing math, roughly: if each preview takes 1 GB of memory and you might have 5 PRs open at once plus production, you want 8 GB+ of RAM on the host. The 8 GB Coolify VPS at LumaDock fits this comfortably.

Step 7, test the flow

Open a real test PR. Make a trivial change (typo fix, comment update), commit to a feature branch, push, open a PR against your main branch. Within seconds Coolify should react.

What you should see in the order it happens:

  1. GitHub posts a check status on the PR showing "Coolify is deploying"
  2. Coolify clones the branch, runs the build (you can watch live in the application's Preview Deployments sub-tab)
  3. The container starts at the preview URL
  4. Coolify posts a comment on the PR with the URL: "Your preview is ready at https://pr-NUMBER.preview.yourdomain.com"
  5. Hitting that URL serves your built app

If any step fails, the most common causes are:

The Coolify comment doesn't appear on the PR, the GitHub App doesn't have permission to write PR comments or the App isn't installed on this specific repo. Re-check the App's permissions on GitHub, re-install on the repo if needed.

The build never starts, the webhook isn't reaching Coolify. Check the GitHub App's "Recent Deliveries" page (Settings → Developer settings → GitHub Apps → Your App → Advanced), look for failed delivery attempts. If they're failing, the webhook URL is wrong (Coolify regenerates it on Source recreation) or your firewall is blocking GitHub's webhook IPs.

The build runs but the URL returns 404, wildcard DNS isn't propagated yet or the wildcard pattern in Coolify doesn't match the actual subdomain Coolify is generating. Double-check both and run dig against the specific preview subdomain to see what DNS returns.

The build runs but the URL returns 502, the container started but isn't responding on the port Coolify expects. Check the build logs, then the container logs in the Preview Deployments tab.

Combining preview deployments with GitHub Actions

If you want CI to run tests before the preview deploys (so you don't waste resources building a preview that the test suite would have caught), use GitHub Actions as a gate. The pattern is:

  1. PR opened triggers GitHub Actions workflow
  2. Workflow runs tests (lint, type check, unit tests, possibly e2e)
  3. If tests pass, workflow calls Coolify's deploy webhook with the PR information
  4. If tests fail, no preview is built

To wire this up, disable Coolify's auto-deploy on PR (so it doesn't fire on every push). In your GitHub Actions workflow, after the tests pass, call the Coolify deploy webhook with a curl command. Coolify shows you the webhook URL in the application's Webhooks section.

The GitHub Actions CI/CD guide covers the test-then-deploy pattern in detail. The same approach works for Coolify, just swap the SSH-based deploy step for a curl call.

Cleaning up after merged or closed PRs

With Auto-stop on PR close enabled, Coolify automatically tears down the preview when the PR is merged or closed. The container is removed, the preview database (if you used the shared preview Postgres approach) keeps its data for the next PR.

If you ever end up with orphaned preview environments (the auto-stop hook misfired, the PR was deleted before close), Coolify lists active preview deploys in the application's Preview Deployments tab. You can manually stop and remove any that are stuck.

For very long-running PRs (a refactor that lives for weeks), the preview environment stays up the whole time, which is usually fine but ties up a slot of resources. If you have a lot of long PRs, bumping your VPS RAM is the cleanest fix.

Common errors and how to fix them

Preview URLs work locally but return ERR_CERT_AUTHORITY_INVALID in browsers

Coolify hasn't issued a Let's Encrypt cert for the wildcard yet. Let's Encrypt's HTTP-01 challenge works for individual subdomains but the cleaner setup is a wildcard cert via DNS-01 challenge. Coolify supports DNS-01 in v4 if you configure it with API credentials for your DNS provider. The Traefik knowledge base in the Coolify docs covers wildcard cert setup. Without DNS-01, each preview URL gets its own individual cert, which works but takes a few extra seconds on first request.

Previews share the production database against your wishes

Your DATABASE_URL isn't scoped per environment or your code isn't reading the preview-specific variable. Verify two things, the production-only variable in Coolify is actually marked as production-only (not "Both") and your code reads the right URL based on the environment. Coolify sets COOLIFY_PREVIEW=true on preview deployments, you can use this to branch in code.

PR previews fall over due to OOM

Either your resource limits are too low (the build can't fit in the limit you set) or you have too many PRs open simultaneously and the cumulative limit exceeds your VPS RAM. Bump the per-preview memory limit or upgrade the VPS or close some old PRs. The heap memory fix guide has more on the build memory side.

Build runs every time someone pushes to the PR branch

That's the intended behavior, every push triggers a fresh build. If you want to slow it down (because builds are expensive and you're rapid-pushing during a debug session), the cleanest fix is to draft the PR (push to a draft PR doesn't trigger builds in Coolify by default in v4). Or push to a side branch first, then PR when you're ready.

The PR comment from Coolify is missing or stuck saying "deploying"

The GitHub App's permission to write to issues/PRs is broken. Go to your GitHub App settings, check the "Permissions" tab matches what Coolify needs, re-install on the repo. Sometimes a recent GitHub permissions change requires an explicit re-install even though the App is technically still authorized.

What next?

Once previews are working reliably, the next things to wire up are CI for gating, comprehensive monitoring (the Prometheus and Grafana guide covers a self-hosted monitoring stack) and zero-downtime production deploys (the zero-downtime deploys guide covers patterns for Node.js apps that apply to Coolify-managed deployments).

If you're using preview deployments heavily, also set up automated backups for the preview database so a destructive PR can't permanently corrupt your test data, the Coolify backups guide covers the setup.

Your idea deserves better hosting

24/7 support 30-day money-back guarantee Cancel anytime
Billing Cycle

1 GB RAM VPS

£2.97 Save  25 %
£2.23 Monthly
  • 1 vCPU AMD EPYC
  • 30 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Firewall management
  • Free server monitoring

2 GB RAM VPS

£4.46 Save  17 %
£3.72 Monthly
  • 2 vCPU AMD EPYC
  • 30 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Firewall management
  • Free server monitoring

6 GB RAM VPS

£11.16 Save  33 %
£7.44 Monthly
  • 6 vCPU AMD EPYC
  • 70 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Firewall management
  • Free server monitoring

AMD EPYC VPS.P1

£5.95 Save  25 %
£4.46 Monthly
  • 2 vCPU AMD EPYC
  • 4 GB RAM memory
  • 40 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

AMD EPYC VPS.P2

£11.16 Save  27 %
£8.19 Monthly
  • 2 vCPU AMD EPYC
  • 8 GB RAM memory
  • 80 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

AMD EPYC VPS.P4

£22.34 Save  20 %
£17.87 Monthly
  • 4 vCPU AMD EPYC
  • 16 GB RAM memory
  • 160 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

AMD EPYC VPS.P5

£27.18 Save  21 %
£21.59 Monthly
  • 8 vCPU AMD EPYC
  • 16 GB RAM memory
  • 180 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

AMD EPYC VPS.P6

£42.45 Save  21 %
£33.51 Monthly
  • 8 vCPU AMD EPYC
  • 32 GB RAM memory
  • 200 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

AMD EPYC VPS.P7

£52.13 Save  20 %
£41.70 Monthly
  • 16 vCPU AMD EPYC
  • 32 GB RAM memory
  • 240 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

EPYC Genoa VPS.G1

£3.72 Save  20 %
£2.97 Monthly
  • 1 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4th generation 9xx4 with 3.25 GHz or similar, on Zen 4 architecture.
  • 1 GB DDR5 memory
  • 25 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

EPYC Genoa VPS.G2

£9.67 Save  23 %
£7.44 Monthly
  • 2 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4th generation 9xx4 with 3.25 GHz or similar, on Zen 4 architecture.
  • 4 GB DDR5 memory
  • 50 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

EPYC Genoa VPS.G4

£19.36 Save  27 %
£14.14 Monthly
  • 4 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4th generation 9xx4 with 3.25 GHz or similar, on Zen 4 architecture.
  • 8 GB DDR5 memory
  • 100 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

EPYC Genoa VPS.G6

£36.49 Save  31 %
£25.32 Monthly
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4th generation 9xx4 with 3.25 GHz or similar, on Zen 4 architecture.
  • 16 GB DDR5 memory
  • 200 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

EPYC Genoa VPS.G7

£55.85 Save  27 %
£40.96 Monthly
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4th generation 9xx4 with 3.25 GHz or similar, on Zen 4 architecture.
  • 32 GB DDR5 memory
  • 250 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

1 vCPU AMD Ryzen 9

£10.42 Save  29 %
£7.44 Monthly
  • Dedicated CPU 4.5GHz AMD Ryzen 9 7950X with a native CPU frequency of 4.5 GHz.
  • 4 GB DDR5 memory
  • 50 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

2 vCPU AMD Ryzen 9

£19.36 Save  19 %
£15.63 Monthly
  • Dedicated CPU 4.5 GHz AMD Ryzen 9 7950X with a native frequency of 4.5 GHz.
  • 8 GB DDR5 memory
  • 100 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

8 vCPU AMD Ryzen 9

£69.26 Save  30 %
£48.40 Monthly
  • Dedicated CPU 4.5 GHz AMD Ryzen 9 7950X with a native frequency of 4.5 GHz.
  • 32 GB DDR5 memory
  • 400 GB NVMe storage
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • 1 Gbps network
  • Automatic backup included
  • Firewall management
  • Free server monitoring

Frequently asked questions

How do I enable PR preview deployments in Coolify?

Three steps. First, connect your repo through a GitHub App as a Coolify Source (not a personal access token, those don't get the right webhook events). Second, set up wildcard DNS pointing at your VPS for whatever pattern you want preview URLs to use, like *.preview.yourdomain.com. Third, in your application's settings find the Preview Deployments section, toggle it on, set the domain pattern to pr-{pr_number}.preview.yourdomain.com and enable auto-deploy on PR open and auto-stop on PR close. From then on, every PR opened against your main branch gets its own preview environment.

Stop installing. Start shipping.

LumaDock Coolify plans come with the dashboard pre-installed, unmetered bandwidth and a flat monthly bill. Try the server risk free with a 30-day refund guarantee.

GPU products are in high demand at the moment. Fill the form to get notified as soon as your preferred GPU server is back in stock.