Create Environments with Masked Production Data Using Neon Branches
Use branching and PostgreSQL Anonymizer to safely replicate entire environments in seconds, even with PII in production
Every engineering team needs realistic, reliable environments to test, debug, and ship software with confidence. The ideal setup sounds simple – clone your production database, run your tests, and move on.
But “clone your production database” is easier said than done. Replicating a live Postgres environment means provisioning new instances and running manual dump/restore processes. Most developers end up working on a database that’s already out of sync with production.
This problem gets even worse when your production database contains personal data (PII) – customer names, emails, payment details, and so on. Now, on top of the already infrastructure-heavy process of provisioning new databases, you also have to figure out how to safely populate them.
Why Cloning Production Environments Is Still Hard
To summarize:
- Provisioning environments is slow and manual. Spinning up a new instance, creating a fresh database, and populating it with data (real or fake) takes time and effort, together with coordination across teams and DBAs.
- PII makes cloning a non-starter. Regulatory frameworks like GDPR and HIPAA, internal security controls, and the practical risk of accidentally triggering real email or analytics flow make it impossible to copy production data into dev or test environments.
- Seed data doesn’t cut it. But it’s hard to populate non-prod environment with “fake data” in a way that truly mirrors production. The result is often an unrealistic environment that rarely keeps up with schema or business logic changes, and many bugs slip.
- Environments drift constantly. As the environments add up, staging lags behind prod, dev diverges from staging, and no one’s quite sure what version the tests are running against.
“Our testing process was very manual before. Product would create a test customer in our development environment, then generate PDFs; the QA team would test and manually run through all the math; then an engineer would have to go into the database, look at all the values, and handwrite them into fixtures for our end-to-end tests… That’s multiple days for every single change” (Miguel Hernandez, Backend Tech Lead at Neo.Tax)
All of this points to one missing primitive – a safe, fast, and repeatable way to create production-like environments without exposing real user data. This is what we’re working on in Neon.
The Solution: Branches + PostgreSQL Anonymizer in Neon
Solving this problem requires two things:
- A way to clone production data without the operational overhead of managing new instances
- A way to anonymize sensitive information before it reaches development or testing environments
Neon gives you both:
Neon branches – Instant, production-grade copies of your DB

In Neon, a branch is a lightweight, copy-on-write clone of a Postgres database. It contains the same schema and data as its parent, but diverges safely in isolation, with its own compute endpoint and connection URL.
Branches are created instantly no matter how large the dataset and don’t require provisioning a new instance or duplicating storage, it’s all built into Neon’s architecture. They remain “logically connected’ to the parent, so production drift is a thing of the past – child branches can be synced with their parent in one API call, and they’ll reflect up-to-date and schema again.
Teams use branches where they were using redundant instances or local setups before:
- Spin up isolated environments for testing, development, or CI
- Work with a snapshot of production without affecting production
- Reproduce issues, test migrations, or preview features in realistic conditions
“With Neon branches we get a totally isolated copy to test code changes even when they include database migrations. We can test all changes in real data and ensure that by the time we actually merge the PR to main, things really work” (Avi Romanoff, Founder at Magic Circle)
PostgreSQL Anonymizer – Protect PII with static masking

Neon branches eliminate the overhead of environment setup – no need to provision a new instance, create a new database, populate it, or worry about environments drifting out of sync. But if your production database contains PII, branching it directly is not an option.
That’s where the PostgreSQL Anonymizer extension (anon) comes in. This open-source extension lets you define masking rules on sensitive columns in your database, replacing real values with fake but realistic-looking alternatives. Neon currently supports static masking, meaning the data is permanently rewritten on the branch.
You can choose from multiple masking strategies, such as
anon.fake
: replaces real values with random onesanon.partial
: masks part of a field, like a credit card or phone numberanon.noise: adds
variability to numerical data
Here’s a simple example using the users
table. Suppose we have this data:
SELECT * FROM users LIMIT 3;
id | first_name | last_name | email | iban
----+--------------+-------------+------------------------------+-------
1 | Real First Name 1 | Real Last Name 1 | user1@example.com | REALIBAN1
2 | Real First Name 2 | Real Last Name 2 | user2@example.com | REALIBAN2
3 | Real First Name 3 | Real Last Name 3 | user3@example.com | REALIBAN3
To anonymize this data, we apply masking rules using the faking strategy (anon.fake
). This strategy replaces real values with randomly generated, fake but realistic data using functions provided by the extension:
SECURITY LABEL FOR anon ON COLUMN users.first_name IS 'MASKED WITH FUNCTION anon.fake_first_name()';
SECURITY LABEL FOR anon ON COLUMN users.last_name IS 'MASKED WITH FUNCTION anon.fake_last_name()';
SECURITY LABEL FOR anon ON COLUMN users.email IS 'MASKED WITH FUNCTION anon.fake_email()';
SECURITY LABEL FOR anon ON COLUMN users.iban IS 'MASKED WITH FUNCTION anon.fake_iban()';
After running the anonymization command,
SELECT anon.init();
SELECT anon.anonymize_database();
You would see something like this when querying the table again:
SELECT * FROM users LIMIT 3;
id | first_name | last_name | email | iban
----+------------+-----------+------------------------+-----------------------------
1 | Rhonda | Alvarado | bryanalan@example.net | GB34QDZL89198122631902
2 | Darius | Reyes | brandon57@example.com | GB96LBQE53732061681569
3 | Stefanie | Byrd | barbara40@example.com | GB67CAZQ75813049489060
The real data is gone, replaced by randomly generated values using PostgreSQL Anonymizer’s built-in faker functions. This approach is far more realistic and scalable than using handcrafted seed data. Seed files are hard to maintain and rarely reflect your actual production schema. As your app evolves with new tables, columns, relationships, your seed data drifts behind.
The anon extension takes care of masking sensitive data while preserving the structure, schema, and referential integrity of the database. Foreign keys, data types, and relationships remain intact, making the anonymized database safe but fully usable as a production clone.
The Workflow: Create Once, Reuse Everywhere
Once anonymized, the anon branch behaves just like any other Neon branch, and it can serve as the template for all your non-prod environments. You can branch off of it instantly to create as many environments as you’d like.
The workflow is simple:
- Create a branch from your production database. This branch is a copy-on-write snapshot of prod, schema, data, and all, but fully isolated and safe to modify.
- Anonymize the branch using masking rules. Apply column-level masking via the PostgreSQL Anonymizer extension. You can do this manually (with SQL commands) or automatically (with a GitHub Action that runs on pull request creation).
- Use the anonymized branch as your base. Now that your sensitive data is masked, you can branch from this anonymized copy as many times as you want for any non-prod use case, for example:
- Per-PR preview environments. Spin up a database per pull request with realistic test data, without any risk of exposing real users’ information
- CI pipelines. Test against real schema and realistic data while staying compliant and secure
- Ephemeral environments. Create and discard environments freely as part of your dev workflow
- Local development environments. Give every engineer a realistic sandbox
- Contractor or partner access. Share a branch of your database for testing or demos, without giving access to actual user data
- Safe debugging. Reproduce bugs or verify migrations using realistic anonymized data, without worrying about leaks
Since these branches are all copy-on-write, the data overhead is minimal, and the experience is fast.
Automate this workflow
Start Building with Anonymized Branches in Neon
Cloning production environments has always been a pain, especially when sensitive data is involved. But by combining Neon’s branching architecture and the PostgreSQL Anonymizer extension it’s much easier to create safe, production-like environments. Just branch from production, anonymize once, reuse everywhere.
To get started, sign up to Neon and follow this guide. If you have any questions on how to integrate anonymized branching workflows into your environment, reach out to our team.