Argo CD PR Generator vs a Managed Platform for Kubernetes Preview Environments: the Full Build Sheet
The Argo CD ApplicationSet pull request generator gives you about 40 lines of YAML per preview. This build sheet shows the real YAML for it and for everything you still own around it (image builds, a database per PR, secrets, DNS and TLS, PR comments, cleanup and cost caps), what breaks after a month, and when a managed platform is the better trade.
The Argo CD ApplicationSet pull request generator turns each open PR into an Argo CD Application on GitHub, GitLab, Gitea, Bitbucket Server, Bitbucket Cloud or Azure DevOps, and deletes it when the PR closes or stops matching. It polls every 30 minutes by default unless you expose the ApplicationSet webhook server, which needs its own Ingress.
The generator deploys manifests at a commit. A usable preview needs seven more components: an image build gate, a database per PR, secrets, wildcard DNS and TLS, the URL posted on the PR, cleanup of what Argo CD leaves behind, and cost caps. Each one has a failure mode that shows up around day 30.
Three things leak by default: PersistentVolumeClaims from StatefulSets (Kubernetes keeps them unless you change persistentVolumeClaimRetentionPolicy), namespaces you did not declare as manifests, and previews for PRs that stay open for weeks.
Keep the PR generator when your stack is already Helm or Kustomize, Git must stay the only source of truth, and a platform engineer owns the glue. Pick a managed platform when nobody wants to own that glue, previews need real databases and per-PR secrets, or coding agents open PRs faster than humans clean them up.
Qovery is the managed option that runs in your own cloud account or on your existing cluster. It clones a blueprint environment per PR, builds images, provisions the database, posts the URL and deletes everything on merge. Its Argo CD integration can show your existing Argo CD applications next to the previews, so you do not have to migrate production to try it.
The Argo CD pull request generator is about 40 lines of YAML. A preview environment a reviewer trusts is closer to ten components, and the generator is one of them.
I have seen the same project play out in several platform teams: someone wires up an ApplicationSet with a pullRequest generator on a Friday, the first preview shows up on Monday, and then the next six weeks go into images that do not exist yet, databases that never get deleted, certificates that hit rate limits and namespaces nobody remembers creating. Argo CD did its job the whole time; the rest is the size of the problem.
We already wrote a short comparison of Argo CD and Qovery Free for agent PR environments. This article is the build sheet behind it: the real YAML for the generator, the real YAML for each piece you still own, what breaks after a month, and a fair way to decide between building it and using a managed platform.
What does the Argo CD ApplicationSet pull request generator actually do?
The pull request generator lists open pull requests through your Git provider's API and renders one Argo CD Application per PR from a template. When a PR closes or no longer matches your filters, the Application is removed, and by default Argo CD deletes the resources it deployed.
Filters:branchMatch, targetBranchMatch and titleMatch regular expressions, plus label filtering on GitHub and GitLab. A PR must carry all listed labels to match.
Polling: the controller checks every requeueAfterSeconds, which defaults to 1800 seconds. That is a 30-minute wait between "PR opened" and "preview exists" unless you set up the webhook.
Deletion: Applications generated by an ApplicationSet carry the resources-finalizer.argocd.argoproj.io finalizer, so their Kubernetes resources are deleted with them, unless you set preserveResourcesOnDeletion: true (Application deletion docs).
Here is the ApplicationSet I would start from in November 2026. It authenticates as a GitHub App rather than a personal token, only picks up PRs that carry a preview label, and pins everything to the PR's head commit:
YAML
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: shop-previews
namespace: argocd
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- pullRequest:
github:
owner: acme
repo: shop
appSecretName: github-app-repo-creds # GitHub App, not a PAT
labels:
- preview # set by CI once images exist
filters:
- targetBranchMatch: "^main$"
requeueAfterSeconds: 600 # safety net; the webhook does the real work
template:
metadata:
name: 'shop-pr-{{.number}}'
labels:
preview: "true"
pr: '{{.number}}'
spec:
project: previews # never templated, see Security below
source:
repoURL: https://github.com/acme/shop.git
targetRevision: '{{.head_sha}}'
path: deploy/chart
helm:
releaseName: shop
valuesObject:
image:
tag: 'pr-{{.number}}-{{.head_short_sha}}'
ingress:
host: 'pr-{{.number}}.preview.acme.dev'
postgres:
enabled: true # per-PR database, see below
destination:
server: https://kubernetes.default.svc
namespace: 'shop-pr-{{.number}}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Two details from the docs change how you deploy this.
The ApplicationSet webhook is separate from the Argo CD API webhook. The ApplicationSet controller runs its own webhook server, exposed as a ClusterIP Service, and the docs say you need "an ApplicationSet specific Ingress resource" to reach it. GitHub then sends opened, closed, reopened, labeled, unlabeled and synchronize events to /api/webhook. The Service is argocd-applicationset-controller on port 7000 in the upstream install manifest:
Only admins should create ApplicationSets. The ApplicationSet security docs warn that a templated project field in a PR-driven ApplicationSet can let a PR author grant themselves management of out-of-bounds resources. Hardcode project: previews and give that AppProject a destination limited to shop-pr-* namespaces.
The generator is well designed and free, and it is the smallest part of the work.
What do you still have to build around the PR generator?
You still own seven components: an image build gate, a database per PR, secrets, wildcard DNS and TLS, the PR comment with the URL, cleanup of what the finalizer does not catch, and cost guardrails. Each comes from a different open source project with its own upgrade cycle.
Component
What it does
Typical tool
Who owns it with the PR generator
Day-30 failure mode
Image build gate
Builds pr-<n>-<sha> before Argo CD syncs
GitHub Actions, GitLab CI, Buildkite
Your CI
Pods in ImagePullBackOff when Argo CD syncs first
Database per PR
Isolates schema migrations per preview
CloudNativePG, a Postgres StatefulSet, a branching service
Your chart
Orphaned volumes that keep billing
Secrets
Sandbox API keys per preview
External Secrets Operator, Sealed Secrets
Your platform team
Production keys copied into previews
DNS and TLS
pr-123.preview.acme.dev with HTTPS
ingress-nginx, cert-manager, external-dns
Your platform team
Let's Encrypt rate limits with per-PR certificates
URL on the PR
Tells reviewers and agents where to click
Argo CD Notifications, a CI step
Your platform team
Comments posted before the preview is healthy
Cleanup
Removes what the finalizer leaves
CronJob, Kyverno cleanup policies, scripts
Your platform team
Stale previews for PRs open for weeks
Cost guardrails
Caps CPU, memory and nodes per preview
ResourceQuota, LimitRange, node pool limits
Your platform team
A load test in one PR starves the others
Here is each one with the YAML I would ship.
1. How do you stop Argo CD from deploying an image that does not exist yet?
Gate the preview on the build. The ApplicationSet above only matches PRs with the preview label, so CI adds that label after the image is pushed. Argo CD never sees a PR whose image is missing.
The tag uses the first 8 characters of the head SHA, which matches the generator's head_short_sha. Get that wrong and every preview pulls a tag that was never built.
The label trick covers the first push. For later pushes the label is already there, so the webhook fires on synchronize and Argo CD can still race your build. In practice teams either accept a few minutes of ImagePullBackOff that heals itself, or remove the label at the start of the job and add it back at the end. Both work, and each has a cost.
2. How do you give every pull request its own database?
Ship the database in the chart, so it lives and dies with the preview namespace. With the CloudNativePG operator, a per-PR Postgres is one custom resource. The operator supports three bootstrap methods: initdb for an empty cluster, recovery from a base backup, and pg_basebackup to clone a live cluster of the same major version (CloudNativePG bootstrap docs).
The operator writes the application credentials to a Secret named shop-db-app, which your Deployment reads. Then you still need seed data that looks real without being copied from production, which is usually a migration plus a fixtures Job with an Argo CD PostSync hook.
Watch the volumes. If you go the plain StatefulSet route instead of an operator, Kubernetes keeps the PVCs when the StatefulSet is deleted. The StatefulSet docs are explicit: deleting or scaling down a StatefulSet "will not delete the volumes associated with the StatefulSet", and the default persistentVolumeClaimRetentionPolicy is Retain. Set it on every preview StatefulSet:
3. How do you inject secrets without copying production keys into previews?
Use one ExternalSecret per preview that reads from a dedicated sandbox path in your secret store. The External Secrets Operator syncs those values into a regular Kubernetes Secret in the PR namespace:
Policy is harder than the YAML. Someone has to decide which third-party sandboxes a preview may call (Stripe test mode, a staging OAuth client, a fake email provider), and how to stop anyone from pointing key: at the production path. On Argo CD, that is an AppProject restriction plus a code review habit, or a Kyverno policy that rejects ExternalSecret objects reading outside previews/.
4. How do you give every preview a URL with valid HTTPS?
Issue one wildcard certificate for *.preview.acme.dev and reuse it in every preview. Wildcards require the ACME DNS-01 challenge; Let's Encrypt states that HTTP-01 cannot issue wildcard certificates (challenge types).
Set it as the ingress controller's default certificate, and add one wildcard DNS record pointing *.preview.acme.dev at the load balancer.
One certificate per preview runs into rate limits. Let's Encrypt allows "up to 50 certificates" per registered domain every 7 days, refilling one every 202 minutes (Let's Encrypt rate limits). A team whose coding agents open 15 PRs a day crosses that in four days, and then production renewals under the same domain compete for the same budget. Use a separate registered domain for previews, like acme-preview.dev, so a busy week never blocks a production certificate.
5. How does the preview URL get back to the pull request?
Use Argo CD Notifications with the GitHub service. Its pullRequestComment block upserts a comment identified by commentTag, so each new push edits the same comment instead of piling up new ones (GitHub service docs):
Subscribe the generated Applications by adding notifications.argoproj.io/subscribe.on-preview-healthy.github: "" to the template's annotations. The service finds the repository and commit from the Application's repoURL and synced revision by default, which works when the chart lives in the app repository, as it does here.
This is also where a coding agent finds out whether its change works. If the agent cannot read the preview's logs, it learns "something failed" and nothing else, unless you give it scoped access to the Argo CD API or to the preview namespaces.
6. What does the finalizer not clean up?
The finalizer deletes the resources Argo CD tracks. Three things fall outside that:
Namespaces created by CreateNamespace=true. Argo CD creates them, but it does not manage them as part of the Application by default; the sync options docs describe a tracking annotation you have to set yourself to opt in. Test what happens on your version, then either render a Namespace manifest in the chart or sweep empty shop-pr-* namespaces on a schedule.
Volumes with a Retain policy, covered above.
PRs that never close. A draft PR left open for six weeks keeps its preview running for six weeks. The generator does exactly what you asked.
A small CronJob handles the last case by removing the preview label from PRs with no push in 72 hours, which makes the generator delete the preview on the next webhook or poll:
The node group's maximum size is your real spending cap, because it bounds the worst case whatever your agents do. Our cost model for a preview per agent PR puts one full-stack preview at about $0.07 an hour on an existing EKS cluster in us-east-1, and shows that lifetime drives the bill more than size: at 30 agent PRs per working day, weekly manual cleanup costs about $7,700 a month, against about $95 with delete-on-merge, schedules, on-demand creation and Spot.
Preview environments per PR, without owning the glue.
Qovery clones a blueprint environment for every pull request on your own AWS, GCP, Azure or Scaleway account, or your existing Kubernetes cluster: images, database, secrets, URL on the PR, and deletion on merge. Keep Argo CD for production if you want.
These are the problems I see show up after a few weeks of real traffic, each with the limit behind it:
Git provider API budget. Every ApplicationSet with a PR generator lists PRs on each poll. A personal access token gets 5,000 requests per hour on GitHub, and GITHUB_TOKEN in Actions gets 1,000 per hour per repository (GitHub REST rate limits). Shorten requeueAfterSeconds across 40 repositories with one token and you will find the ceiling. Use a GitHub App and the webhook.
Certificate budget. Covered above: 50 certificates per registered domain per 7 days.
Argo CD upgrades. Argo CD ships a minor release every three months, and only the three most recent minor versions get patches (release process). v3.6.0 shipped on 3 November 2026 (release notes). Your preview pipeline now has a quarterly upgrade to test, alongside cert-manager, External Secrets and your database operator.
Schema drift. A migration in PR 1842 and a different one in PR 1845 are fine with a database per PR, and broken with a shared staging database. Teams that skip step 2 to save time find this out when two agents work on the same table.
Nobody owns it. The preview system touches CI, Argo CD, DNS, the secret store and the cluster. When it breaks at 6pm, it belongs to whoever built it.
How much does the DIY path really cost?
The software is free, so the cost is engineering time. I will not invent a benchmark for how long this takes, because it depends heavily on what you already run. Here is the worksheet I use with teams instead, with assumptions you replace with your own:
Line item
What drives it
Example assumption to replace
Initial build
Seven components above, wired and tested
3 to 6 engineer-weeks if ingress, cert-manager and a secret store already exist
Upgrades
Argo CD every 3 months, plus cert-manager, ESO, the DB operator
Every new service needs chart values, secrets and a DB decision
Half a day each
Cloud spend
Preview compute and storage
Your node group cap, see the cost model above
Multiply engineer-weeks by your own fully loaded cost and compare it with the license of whatever you would buy. If you already have a platform team that builds this kind of thing for fun, the DIY path is often cheaper. If the person who would build it is also your only SRE, it usually is not.
Argo CD PR generator vs a managed platform: how do they compare?
A managed preview platform owns the seven components above as product features; the PR generator owns one and leaves the rest to you. The trade is control and zero license cost against weeks of platform work and a permanent maintenance line.
Your own AWS, GCP, Azure or Scaleway account, or an existing cluster (EKS, GKE, AKS, k3s, on-premise)
Preview definition
Helm chart or Kustomize in Git
A blueprint environment (Console, CLI, Terraform provider)
Trigger
Webhook or 30-minute poll, filtered by labels and regex
Every PR on the blueprint's base branch, or on demand with a PR comment
Image build per commit
Your CI
Built by Qovery from the PR branch
Database per PR
Your chart or operator
Container database cloned per preview, or shared
Secrets
External Secrets or Sealed Secrets, your policy
Variables and secrets cloned from the blueprint
DNS and TLS
Your ingress, cert-manager, DNS records
URL per preview, ingress and certificates handled
URL on the PR
Argo CD Notifications or a CI step
Posted on the PR
Cleanup
Finalizer, plus your sweeps for namespaces, volumes, stale PRs
Environment deleted on merge or close
Schedules and cost caps
CronJobs, quotas, node group limits
Deployment rules to stop previews on a schedule, plus your node group limits
Access for coding agents
Git only, unless you grant Argo CD API or cluster access
MCP Server (read-only by default), CLI, Agent Skills, OPA-based API policy tokens
Audit
Git history, Argo CD events, Kubernetes audit logs
Qovery audit logs plus Git history
Qovery is not the only managed option. Bunnyshell and Northflank also build full preview environments from PRs, and Signadot takes a different approach with request-routed sandboxes that share a baseline cluster instead of cloning the whole stack. Our comparison of 8 per-PR environment tools on Kubernetes covers them side by side, including Flux, vCluster, Helm and Kustomize.
When should you keep the Argo CD PR generator?
Keep it when you already have most of the surrounding pieces and someone owns them. Concretely, when most of these are true:
Your services are already packaged as Helm charts or Kustomize, and previews must render exactly what production renders.
Git must be the only source of truth for anything that reaches a cluster, for audit or for culture.
ingress, cert-manager, a secret store and a database operator already run in the cluster for other reasons, so the marginal cost is wiring them together.
A platform engineer owns preview environments as a product, with time set aside for upgrades.
You want no vendor in the loop at any price.
In that situation the PR generator is the right tool, and the YAML above is most of what you need.
When is a managed platform the better trade?
A managed platform wins when the surrounding glue costs you more than the deployment itself. That is usually the case when:
Nobody wants to own the seven components. The team wants previews and has no appetite for running a preview system.
Previews need real databases and per-PR secrets from day one, because PRs change schemas.
Coding agents open PRs faster than humans close them. Claude Code, Cursor and Codex each end tasks with a PR, and every PR is a preview to create, update and delete.
Agents should debug their own previews. Reading logs and redeploying after a fix needs scoped access, which is hard to grant safely through raw cluster credentials.
The stack is not packaged as charts yet. A few services with Dockerfiles become a blueprint without writing Kubernetes YAML first.
This is where Qovery fits. Qovery is an Agentic DevOps platform: the control plane is Qovery's, and every preview runs on Kubernetes in your own cloud account or on the cluster you already run. You describe the stack once as a blueprint environment, turn on preview environments, and each PR gets a clone with its images built from the PR branch, its database, its variables and secrets, and a URL on the PR. Merge or close deletes it. Deployment rules stop previews outside working hours.
For agents, the MCP Server is read-only by default, and an API Policy Token attaches an Open Policy Agent policy evaluated on every request, so an agent can read logs and redeploy previews in one project and nothing else. Its actions show up in the audit log as policy:<token-id>:<token-name>. The feature is still in preview and its policy input contract may change, so check the docs before you build on it.
Can you keep Argo CD for production and use a managed platform for previews?
Yes, and for many teams that is the sensible split. Production keeps its charts, its reviews and its Argo CD audit story; previews move to a system built for short-lived environments.
Qovery's Argo CD integration connects an Argo CD instance running on a cluster added to Qovery. Your Argo CD applications appear as Qovery environments organized by namespace, with pod status, logs, shell access, sync and health status, and a manifest view. They stay read-only in Qovery: configuration and manifests stay in Git. The docs note that sync and rollback actions are planned and that behaviour may change, so treat it as a single view rather than a second deployment path today.
How do I set up preview environments with Qovery from Claude Code or Cursor?
Install the Qovery Agent Skills, connect the MCP Server, then ask your agent in plain language. These are the only commands you need:
BASH|1. Agent Skills: one install for Claude Code, Cursor, Codex, OpenCode, Gemini CLI
curl -fsSL https://skill.qovery.com/install.sh | bash
# 2. MCP Server for Claude Code: OAuth sign-in, read-only by default
claude mcp add --transport http qovery https://mcp.qovery.com/mcp --callback-port 4242
Step 1 installs the skills, including qovery-onboard, qovery-deploy and qovery-preview. Nothing touches your cloud until you approve an action.
Step 2 opens a browser for you to sign in with OAuth. The server can read environments, logs and deployment status, but cannot change anything.
Then ask: "Use Qovery to set up preview environments for this repository on my cluster, with a Postgres per pull request." The qovery-preview skill detects or creates the blueprint environment and enables previews; you approve each step.
Only if you want the agent to redeploy previews itself, add the write-enabled MCP Server (this also requires write access to be enabled in the Qovery Console):
BASH
claude mcp add --transport http qovery "https://mcp.qovery.com/mcp?read_write=true" --callback-port 4242
Is the Argo CD ApplicationSet pull request generator enough for Kubernetes preview environments?
It is enough to deploy manifests per pull request and delete them when the PR closes. It is not enough for a preview a reviewer can use: you still build images per commit, a database per PR, secrets, wildcard DNS and TLS, the PR comment with the URL, cleanup of namespaces, volumes and stale PRs, and cost caps.
Argo CD ApplicationSet pull request generator vs a managed platform: which should I choose?
Choose the PR generator when your stack is already Helm or Kustomize, Git must be the only source of truth, and a platform engineer owns the surrounding components. Choose a managed platform such as Qovery when nobody wants to own that glue, previews need real databases and secrets per PR, or coding agents open PRs faster than humans clean them up.
How often does the Argo CD pull request generator check for new PRs?
Every 30 minutes by default, through requeueAfterSeconds: 1800. To create previews within seconds, expose the ApplicationSet controller's own webhook server through a dedicated Ingress (Service argocd-applicationset-controller, port 7000, path /api/webhook) and send pull request events from your Git provider.
Does Argo CD delete preview resources when the pull request is closed?
Yes for resources it tracks: generated Applications carry the resources-finalizer.argocd.argoproj.io finalizer unless you set preserveResourcesOnDeletion. Check three things separately: namespaces created through CreateNamespace=true, StatefulSet volumes (Kubernetes keeps them by default), and PRs that stay open for weeks.
Can I use Qovery for preview environments and keep Argo CD for production?
Yes. Qovery runs previews in your own cloud account or on your existing cluster, and its Argo CD integration shows your Argo CD applications as read-only Qovery environments, with logs, pod status and sync status, while deployments stay in Argo CD and Git.
Alessandro leads product at Qovery. He drives the changelog, roadmap, and product strategy - turning customer feedback into platform capabilities.
Next step
Preview environments per PR, without owning the glue.
Qovery clones a blueprint environment for every pull request on your own AWS, GCP, Azure or Scaleway account, or your existing Kubernetes cluster: images, database, secrets, URL on the PR, and deletion on merge. Keep Argo CD for production if you want.