{"id":43200,"date":"2026-02-27T11:08:50","date_gmt":"2026-02-27T10:08:50","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/?p=43200"},"modified":"2026-02-27T11:15:41","modified_gmt":"2026-02-27T10:15:41","slug":"postgresql-anonymizer-simple-data-masking-for-dbas","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/","title":{"rendered":"PostgreSQL Anonymizer: Simple Data Masking for DBAs"},"content":{"rendered":"\n<p>Sensitive data (names, emails, phone numbers, personal identifiers\u2026) should not be freely exposed outside production. When you refresh a production database to a test or staging environment, or when analysts need access to real-looking data, anonymization becomes critical.<\/p>\n\n\n\n<p>The PostgreSQL Anonymizer extension (often called anon) is an open\u2011source extension that lets you mask, fake, shuffle, or generalize data directly inside PostgreSQL, using simple SQL rules. This article explains what it is, how it works, how to install it on PostgreSQL 18.1 running on Red Hat Enterprise Linux 10.1, and how to use it with clear, beginner\u2011friendly examples.<\/p>\n\n\n\n<p>The target audience is everyone, but especially beginner DBAs who want a practical, command\u2011line\u2013focused introduction.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-i-what-is-postgresql-anonymizer\">I. What is PostgreSQL Anonymizer?<\/h2>\n\n\n\n<p>PostgreSQL Anonymizer is an extension that helps protect sensitive data by replacing it with fake or obfuscated values.<\/p>\n\n\n\n<p>Instead of exporting data and anonymizing it with scripts, the rules live inside the database itself. You declare how a column should be anonymized, and PostgreSQL applies that rule automatically.<\/p>\n\n\n\n<p>Typical use cases:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Refreshing production data into test \/ staging environments<\/li>\n\n\n\n<li>Giving developers or analysts access to realistic but non\u2011sensitive data<\/li>\n\n\n\n<li>Producing anonymized database dumps for external sharing<\/li>\n\n\n\n<li>Helping comply with GDPR and other privacy regulations<\/li>\n<\/ul>\n\n\n\n<p>The extension supports three main approaches:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Static masking \u2013 permanently replaces data in tables<\/li>\n\n\n\n<li>Dynamic masking \u2013 masks data on the fly for specific users<\/li>\n\n\n\n<li>Anonymous dumps \u2013 exports an already anonymized dump<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-ii-how-does-it-work\">II. How does it work ?<\/h2>\n\n\n\n<p>PostgreSQL Anonymizer uses PostgreSQL\u2019s security labels mechanism. You attach a label to a column that says: \u201cWhen this data is anonymized, use <em>this function<\/em>.\u201d<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSECURITY LABEL FOR anon ON customer.email IS &#039;MASKED WITH FUNCTION anon.fake_email()&#039;;\n<\/pre><\/div>\n\n\n<p>Once declared:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Static masking rewrites the table using those rules<\/li>\n\n\n\n<li>Dynamic masking rewrites query results for masked users<\/li>\n\n\n\n<li>Dumps automatically apply the same rules<\/li>\n<\/ul>\n\n\n\n<p>The rules stay attached to the schema, not to scripts or applications.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-iii-installing-postgresql-anonymizer-on-rhel-10-1-postgresql-18-1\">III. Installing PostgreSQL Anonymizer on RHEL 10.1 (PostgreSQL 18.1)<\/h2>\n\n\n\n<p>In this guide, PostgreSQL 18.1 is already installed following dbi services standard. PostgreSQL binaries are located in:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\/u01\/app\/postgres\/product\/18\/db_1\/bin<\/li>\n<\/ul>\n\n\n\n<p>The PostgreSQL data directory (PGDATA) is:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\/u02\/pgdata\/18\/demo-cluster<\/li>\n<\/ul>\n\n\n\n<p>We will only focus on installing and enabling the PostgreSQL Anonymizer extension.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-1-install-the-anonymizer-extension\">1. Install the Anonymizer extension<\/h3>\n\n\n\n<p>Since we are installing from source, we&#8217;ll start by installing the necessary prerequisites. We&#8217;ll use cargo to handle the PGRX system requirements. You can find the official documentation <a href=\"https:\/\/postgresql-anonymizer.readthedocs.io\/en\/latest\/INSTALL\/#install-on-redhat-rocky-linux-alma-linux\" id=\"https:\/\/postgresql-anonymizer.readthedocs.io\/en\/latest\/INSTALL\/#install-on-redhat-rocky-linux-alma-linux\">here<\/a>, but keep in mind that I&#8217;ve updated the commands to reflect a more recent version.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n#Cargo install\n\ncurl --proto &#039;=https&#039; --tlsv1.2 -sSf https:\/\/sh.rustup.rs | sh\nsource $HOME\/.cargo\/env\nrustc --version\ncargo --version\n\n# postgresql_anonymizer install\n\ncargo install cargo-pgrx --version 0.16.1 --locked\ncargo pgrx init --pg18 \/u01\/app\/postgres\/product\/18\/db_1\/bin\/pg_config\ngit clone https:\/\/gitlab.com\/dalibo\/postgresql_anonymizer.git\ncd postgresql_anonymizer\/\nmake extension PG_CONFIG=\/u01\/app\/postgres\/product\/18\/db_1\/bin\/pg_config PGVER=pg18\nsudo make install PG_CONFIG=\/u01\/app\/postgres\/product\/18\/db_1\/bin\/pg_config PGVER=pg18\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-2-enable-anonymizer-in-postgresql-conf\">2. Enable Anonymizer in postgresql.conf<\/h3>\n\n\n\n<p>Because PostgreSQL Anonymizer hooks into query execution, it must be loaded at session start. Edit the configuration file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nvi \/u02\/pgdata\/18\/demo-cluster\/postgresql.conf\n<\/pre><\/div>\n\n\n<p>Add or update:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nsession_preload_libraries = &#039;anon&#039;\n<\/pre><\/div>\n\n\n<p>Restart PostgreSQL:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\npg_ctl -D \/u02\/pgdata\/18\/demo-cluster restart\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-3-install-and-enable-the-anonymizer-extension\">3. Install and enable the Anonymizer extension<\/h3>\n\n\n\n<p>Connect as the PostgreSQL superuser:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\npsql -U postgres\n<\/pre><\/div>\n\n\n<p>Create a database:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE DATABASE anonymizer_demo;\n<\/pre><\/div>\n\n\n<p>Create and initialize the extension:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\npostgres=# \\c anonymizer_demo\nYou are now connected to database &quot;anonymizer_demo&quot; as user &quot;postgres&quot;.\nanonymizer_demo=# CREATE EXTENSION anon CASCADE;\nCREATE EXTENSION\n\nanonymizer_demo=# \\dx\n                                  List of installed extensions\n  Name   | Version | Default version |   Schema   |                 Description\n---------+---------+-----------------+------------+---------------------------------------------\n anon    | 3.0.0   | 3.0.0           | public     | Anonymization &amp; Data Masking for PostgreSQL\n plpgsql | 1.0     | 1.0             | pg_catalog | PL\/pgSQL procedural language\n(2 rows)\n\nanonymizer_demo=# SELECT anon.init();\n init\n------\n t\n(1 row)\n<\/pre><\/div>\n\n\n<p>anon.init() loads fake data dictionaries (names, companies, cities, etc.) used by anonymization functions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-iv-demo-anonymizing-a-simple-table\">IV. Demo: anonymizing a simple table<\/h2>\n\n\n\n<p>For this demo, I will keep it simple and create everything inside the postgres database and default schema, but I recommend you to follow the best practices and use a dedicated database, user and schema.<\/p>\n\n\n\n<p>Create sample data:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE TABLE customer (\n  id         SERIAL PRIMARY KEY,\n  first_name TEXT,\n  last_name  TEXT,\n  birthdate  DATE,\n  email      TEXT,\n  company    TEXT\n);\n\nINSERT INTO customer (first_name, last_name, birthdate, email, company) VALUES\n(&#039;Alice&#039;, &#039;Martin&#039;, &#039;1987-02-14&#039;, &#039;alice.martin@example.com&#039;, &#039;Acme Corp&#039;),\n(&#039;Bob&#039;,   &#039;Dupont&#039;, &#039;1979-11-03&#039;, &#039;bob.dupont@example.com&#039;,   &#039;Globex&#039;);\n\nanonymizer_demo=# select * from customer;\n id |  full_name   | birthdate  |          email           |  company\n----+--------------+------------+--------------------------+-----------\n  1 | Alice Martin | 1987-02-14 | alice.martin@example.com | Acme Corp\n  2 | Bob Dupont   | 1979-11-03 | bob.dupont@example.com   | Globex\n(2 rows)\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-1-static-masking-permanent-anonymization\">1. Static masking (permanent anonymization)<\/h3>\n\n\n\n<p>Static masking is a &#8220;fire and forget&#8221; approach where the original sensitive data is physically overwritten on the disk with faked or scrambled values. This process is destructive. Once the data is masked, the original values are gone forever. This should only be performed on non-production environments (like Staging or Dev) or on a backup copy of your database.<\/p>\n\n\n\n<p>Before applying any rules, you must explicitly enable the static masking engine at both the database and role levels. This acts as a safety switch to prevent accidental data loss.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- Enable the extension for the current database\nanonymizer_demo=# ALTER DATABASE anonymizer_demo SET anon.static_masking = TRUE;\nALTER DATABASE\n\n-- Grant the postgres user permission to execute static masking operations\nanonymizer_demo=# ALTER ROLE postgres SET anon.static_masking = TRUE;\nALTER ROLE\n<\/pre><\/div>\n\n\n<p>Next, you define how the data should be transformed. We use <code>SECURITY LABEL<\/code> to attach masking logic to specific columns. This doesn&#8217;t change the data yet; it simply tells the anon extension which functions to use during the anonymization process.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- Replace names with realistic dummy values\nanonymizer_demo=# SECURITY LABEL FOR anon ON COLUMN customer.first_name IS &#039;MASKED WITH FUNCTION anon.dummy_first_name()&#039;;\nSECURITY LABEL\nanonymizer_demo=# SECURITY LABEL FOR anon ON COLUMN customer.last_name IS &#039;MASKED WITH FUNCTION anon.dummy_last_name()&#039;;\nSECURITY LABEL\n\n-- Generate a random date within a specific age range (1950-2000)\nanonymizer_demo=# SECURITY LABEL FOR anon ON column customer.birthdate IS &#039;MASKED WITH FUNCTION anon.random_date_between(&#039;&#039;1950-01-01&#039;&#039;, &#039;&#039;2000-12-31&#039;&#039;)&#039;;\nSECURITY LABEL\n\n-- Generate syntactically correct but fake emails and company names\nanonymizer_demo=# SECURITY LABEL FOR anon ON column customer.email IS &#039;MASKED WITH FUNCTION anon.fake_email()&#039;;\nSECURITY LABEL\nanonymizer_demo=# SECURITY LABEL FOR anon ON column customer.company IS &#039;MASKED WITH FUNCTION anon.fake_company()&#039;;\nSECURITY LABEL\n<\/pre><\/div>\n\n\n<p>This is the final execution step. Running anonymize_database() triggers the engine to scan your rules and overwrite the table data globally. Depending on the size of your database, this may take some time as it performs UPDATE operations on the disk.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nanonymizer_demo=# SELECT anon.anonymize_database();\n anonymize_database\n--------------------\n t\n(1 row)\n<\/pre><\/div>\n\n\n<p>The data rules have now been applied and the data have been anonymized. You can check the result with the following query:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nanonymizer_demo=# SELECT * FROM customer;\n id | birthdate  |        email         |    company     | first_name | last_name\n----+------------+----------------------+----------------+------------+------------\n  1 | 1960-11-30 | lpeters@example.net  | Brown and Sons | Chyna      | Mertz\n  2 | 1997-09-08 | avaughan@example.com | Rice PLC       | Damien     | Williamson\n(2 rows)\n<\/pre><\/div>\n\n\n<p>You can now turn off static masking to continue with the next demo, dynamic masking:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nanonymizer_demo=# ALTER SYSTEM SET anon.static_masking TO off;\nALTER SYSTEM\nanonymizer_demo=# ALTER ROLE postgres SET anon.static_masking TO off;\nALTER ROLE\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-2-dynamic-masking\">2. Dynamic masking<\/h3>\n\n\n\n<p>Dynamic masking allows you to hide sensitive information from specific users (like developers or analysts) while preserving the original data for administrators or the application itself. The masking happens in memory at the moment the query is executed.<\/p>\n\n\n\n<p>First, we tell the database to activate the transparent masking engine. This allows the anon extension to intercept queries from specific roles and apply masking rules before the results are returned.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nanonymizer_demo=# ALTER DATABASE anonymizer_demo SET anon.transparent_dynamic_masking = TRUE;\nALTER DATABASE\n<\/pre><\/div>\n\n\n<p>To see show case masking in action, we will use two types of users: a masked user who sees fake data, and an unmasked user (like the superuser) who sees the actual data stored on disk.<\/p>\n\n\n\n<p>In this step, we create <code>demo_user<\/code> and &#8220;tag&#8221; them with a security label that forces the masking engine to engage whenever they log in.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nanonymizer_demo=# CREATE ROLE demo_user LOGIN;\nCREATE ROLE\nanonymizer_demo=# SECURITY LABEL FOR anon ON ROLE demo_user IS &#039;MASKED&#039;;\nSECURITY LABEL\nanonymizer_demo=# GRANT pg_read_all_data to demo_user;\nGRANT ROLE\n\nanonymizer_demo=# SECURITY LABEL FOR anon ON ROLE demo_user IS &#039;MASKED&#039;;\nSECURITY LABEL\n\n-- As PostgreSQL user:\n-- Ensure the postgres user remains unmasked so we can see the &#039;real&#039; data\n\nanonymizer_demo=# SECURITY LABEL FOR anon ON ROLE postgres IS NULL;\nSECURITY LABEL\n<\/pre><\/div>\n\n\n<p>We don&#8217;t need to redefine our masking rules (for names, emails, etc.) because the SECURITY LABEL definitions we created in the static masking section are still stored in the database schema.<\/p>\n\n\n\n<p>Watch what happens when we query the table as demo_user:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nanonymizer_demo=&gt; select * from customer;\n id | birthdate  |          email           |     company      | first_name | last_name\n----+------------+--------------------------+------------------+------------+-----------\n  1 | 1957-02-07 | walterkristi@example.org | Fernandez-Tucker | Emelie     | Rohan\n  2 | 1950-05-15 | hannah76@example.com     | Leonard Group    | Jane       | Durgan\n(2 rows)\n<\/pre><\/div>\n\n\n<p>If we run the exact same command again, the output changes:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nanonymizer_demo=&gt; select * from customer;\n id | birthdate  |         email          |    company    | first_name | last_name\n----+------------+------------------------+---------------+------------+------------\n  1 | 1963-12-26 | blairpeter@example.com | Simpson Group | Tod        | Balistreri\n  2 | 1971-09-14 | steven27@example.com   | Davis-Hardin  | Sonny      | Wintheiser\n(2 rows)\n<\/pre><\/div>\n\n\n<p>Because the data is being generated &#8220;on-the-fly&#8221; by the masking functions, the results are dynamic. Each request produces a fresh set of data.<\/p>\n\n\n\n<p>Now, let&#8217;s switch back to the postgres user. Since we removed the MASKED label from this role, the engine steps aside and shows us the actual data residing on the disk (which, in this case, is the data we masked statically in the previous step).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nanonymizer_demo=# SELECT * FROM customer;\n id | birthdate  |        email         |    company     | first_name | last_name\n----+------------+----------------------+----------------+------------+------------\n  1 | 1960-11-30 | lpeters@example.net  | Brown and Sons | Chyna      | Mertz\n  2 | 1997-09-08 | avaughan@example.com | Rice PLC       | Damien     | Williamson\n(2 rows)\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-3-anonymized-dump\">3. Anonymized dump<\/h3>\n\n\n\n<p>An Anonymized Dump allows you to export your database into a <code>.sql<\/code> file where the sensitive data is already replaced by fake values. This is incredibly powerful because you can create a &#8220;safe&#8221; backup that can be shared with developers or consultants without ever giving them access to your live production server.<\/p>\n\n\n\n<p>Rather than using a superuser, we create a specific role whose sole purpose is to retrieve the masked version of the data during the export process.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- Create a specialized user for the dump process\nanonymizer_demo=# CREATE ROLE demo_ano_dumper LOGIN PASSWORD &#039;secret&#039;;\nCREATE ROLE\n\n-- Force the masking engine to be active for this user session\nanonymizer_demo=# ALTER ROLE demo_ano_dumper SET anon.transparent_dynamic_masking = TRUE;\nALTER ROLE\n\n-- Apply the MASKED label to the role\nanonymizer_demo=# SECURITY LABEL FOR anon ON ROLE demo_ano_dumper IS &#039;MASKED&#039;;\nSECURITY LABEL\n\n-- Grant permission to read all tables\nanonymizer_demo=# GRANT pg_read_all_data TO demo_ano_dumper;\nGRANT\n<\/pre><\/div>\n\n\n<p>Now, we use the standard pg_dump utility. Because we are logging in as demo_ano_dumper, the anon extension intercepts the data export on-the-fly. We use a few specific flags to ensure the resulting file is clean and doesn&#8217;t contain the masking logic itself:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n\/u01\/app\/postgres\/product\/18\/db_1\/bin\/pg_dump anonymizer_demo --username=demo_ano_dumper --password --no-security-labels --exclude-extension=&quot;anon&quot; --file=anonymized_dump.sql\n<\/pre><\/div>\n\n\n<p><strong><code>--no-security-labels<\/code><\/strong>: Prevents the &#8220;MASKED&#8221; tags from being exported (the new database doesn&#8217;t need to know how the data was masked).<\/p>\n\n\n\n<p><strong><code>--exclude-extension=\"anon\"<\/code><\/strong>: Ensures the recipient doesn&#8217;t need the <code>anon<\/code> extension installed to restore the file.<\/p>\n\n\n\n<p>If you open the generated anonymized_dump.sql file in a text editor, you will see that the COPY commands contain the fake data, not the original sensitive information.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n--\n-- Data for Name: customer; Type: TABLE DATA; Schema: public; Owner: postgres\n--\n\nCOPY public.customer (id, birthdate, email, company, first_name, last_name) FROM stdin;\n1       1980-12-17      cannonlauren@example.com        Carr-Doyle      Katharina       Shanahan\n2       1998-03-27      sfox@example.net        Pollard and Sons        Ayla    Spencer\n\\.\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-v-conclusion\">V. Conclusion<\/h2>\n\n\n\n<p>In modern development, the goal is to work with realistic data without the real-world risk. PostgreSQL Anonymizer bridges this gap by allowing you to transform sensitive production information into safe, functional datasets.<\/p>\n\n\n\n<p>Now that we&#8217;ve explained how to use pg_anonymizer and covered all three methods, here is a quick guide on when to use each:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Static Masking:<\/strong> Best for &#8220;cleaning&#8221; a staging database after a production refresh.<\/li>\n\n\n\n<li><strong>Dynamic Masking:<\/strong> Best for internal users (DBAs, support staff) who need to work on the live database but shouldn&#8217;t see production data.<\/li>\n\n\n\n<li><strong>Anonymized Dump:<\/strong> Best for sharing data with external partners or creating local development environments.<\/li>\n<\/ul>\n\n\n\n<p>In the end, whether you choose Static, Dynamic, or Dump masking, the benefits remain the same:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Utility:<\/strong> Because the data is masked with realistic functions (like <code>fake_email<\/code> or <code>dummy_first_name<\/code>), your application logic\u2014like email validation or UI layout\u2014still works perfectly.<\/li>\n\n\n\n<li><strong>Compliance:<\/strong> Meet GDPR, HIPAA, and internal security requirements by default.<\/li>\n\n\n\n<li><strong>Safety:<\/strong> Developers and analysts can work on real-world bugs and features without ever seeing a customer&#8217;s actual PII (Personally Identifiable Information).<\/li>\n<\/ul>\n\n\n\n<p>I hope you enjoyed this guide and found these examples clear and easy to follow! My goal was to show that data privacy doesn&#8217;t have to be painful for your development workflow. Don&#8217;t forget to follow the extension latest news on the official website: <a href=\"https:\/\/postgresql-anonymizer.readthedocs.io\/en\/latest\/\">https:\/\/postgresql-anonymizer.readthedocs.io\/en\/latest\/<\/a><\/p>\n\n\n\n<p>If you have any questions about these commands or how to implement them in your own environment, feel free to reach out \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sensitive data (names, emails, phone numbers, personal identifiers\u2026) should not be freely exposed outside production. When you refresh a production database to a test or staging environment, or when analysts need access to real-looking data, anonymization becomes critical. The PostgreSQL Anonymizer extension (often called anon) is an open\u2011source extension that lets you mask, fake, shuffle, [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":43221,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[83],"tags":[3885,3775,3886,77],"type_dbi":[2749],"class_list":["post-43200","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-postgresql","tag-anon","tag-anonymization","tag-pg_anonymizer","tag-postgresql","type-postgresql"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.2 (Yoast SEO v27.2) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>PostgreSQL Anonymizer: Simple Data Masking for DBAs - dbi Blog<\/title>\n<meta name=\"description\" content=\"Learn how to use PostgreSQL Anonymizer for static masking, dynamic masking, and anonymized dumps. Protect your production data while keeping your data functional.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"PostgreSQL Anonymizer: Simple Data Masking for DBAs\" \/>\n<meta property=\"og:description\" content=\"Learn how to use PostgreSQL Anonymizer for static masking, dynamic masking, and anonymized dumps. Protect your production data while keeping your data functional.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-02-27T10:08:50+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-02-27T10:15:41+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"726\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Joan Frey\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Joan Frey\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/\"},\"author\":{\"name\":\"Joan Frey\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/c03c47649664fe73b27ce457e99f5b06\"},\"headline\":\"PostgreSQL Anonymizer: Simple Data Masking for DBAs\",\"datePublished\":\"2026-02-27T10:08:50+00:00\",\"dateModified\":\"2026-02-27T10:15:41+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/\"},\"wordCount\":1417,\"commentCount\":0,\"image\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png\",\"keywords\":[\"anon\",\"anonymization\",\"pg_anonymizer\",\"PostgreSQL\"],\"articleSection\":[\"PostgreSQL\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/\",\"name\":\"PostgreSQL Anonymizer: Simple Data Masking for DBAs - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png\",\"datePublished\":\"2026-02-27T10:08:50+00:00\",\"dateModified\":\"2026-02-27T10:15:41+00:00\",\"author\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/c03c47649664fe73b27ce457e99f5b06\"},\"description\":\"Learn how to use PostgreSQL Anonymizer for static masking, dynamic masking, and anonymized dumps. Protect your production data while keeping your data functional.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#primaryimage\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png\",\"contentUrl\":\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png\",\"width\":1024,\"height\":726},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/www.dbi-services.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PostgreSQL Anonymizer: Simple Data Masking for DBAs\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/c03c47649664fe73b27ce457e99f5b06\",\"name\":\"Joan Frey\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/1e650cf665b4d44dd186355827c0b049d2f95c8cbb45fd10d4e7cb255be67ecb?s=96&d=mm&r=g\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/1e650cf665b4d44dd186355827c0b049d2f95c8cbb45fd10d4e7cb255be67ecb?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/1e650cf665b4d44dd186355827c0b049d2f95c8cbb45fd10d4e7cb255be67ecb?s=96&d=mm&r=g\",\"caption\":\"Joan Frey\"},\"url\":\"https:\/\/www.dbi-services.com\/blog\/author\/joanfrey\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"PostgreSQL Anonymizer: Simple Data Masking for DBAs - dbi Blog","description":"Learn how to use PostgreSQL Anonymizer for static masking, dynamic masking, and anonymized dumps. Protect your production data while keeping your data functional.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/","og_locale":"en_US","og_type":"article","og_title":"PostgreSQL Anonymizer: Simple Data Masking for DBAs","og_description":"Learn how to use PostgreSQL Anonymizer for static masking, dynamic masking, and anonymized dumps. Protect your production data while keeping your data functional.","og_url":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/","og_site_name":"dbi Blog","article_published_time":"2026-02-27T10:08:50+00:00","article_modified_time":"2026-02-27T10:15:41+00:00","og_image":[{"width":1024,"height":726,"url":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png","type":"image\/png"}],"author":"Joan Frey","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Joan Frey","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/"},"author":{"name":"Joan Frey","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/c03c47649664fe73b27ce457e99f5b06"},"headline":"PostgreSQL Anonymizer: Simple Data Masking for DBAs","datePublished":"2026-02-27T10:08:50+00:00","dateModified":"2026-02-27T10:15:41+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/"},"wordCount":1417,"commentCount":0,"image":{"@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#primaryimage"},"thumbnailUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png","keywords":["anon","anonymization","pg_anonymizer","PostgreSQL"],"articleSection":["PostgreSQL"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/","url":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/","name":"PostgreSQL Anonymizer: Simple Data Masking for DBAs - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#primaryimage"},"image":{"@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#primaryimage"},"thumbnailUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png","datePublished":"2026-02-27T10:08:50+00:00","dateModified":"2026-02-27T10:15:41+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/c03c47649664fe73b27ce457e99f5b06"},"description":"Learn how to use PostgreSQL Anonymizer for static masking, dynamic masking, and anonymized dumps. Protect your production data while keeping your data functional.","breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#primaryimage","url":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png","contentUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/02\/Gemini_Generated_Image_gzxnaigzxnaigzxn.png","width":1024,"height":726},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-anonymizer-simple-data-masking-for-dbas\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"PostgreSQL Anonymizer: Simple Data Masking for DBAs"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/c03c47649664fe73b27ce457e99f5b06","name":"Joan Frey","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/1e650cf665b4d44dd186355827c0b049d2f95c8cbb45fd10d4e7cb255be67ecb?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1e650cf665b4d44dd186355827c0b049d2f95c8cbb45fd10d4e7cb255be67ecb?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1e650cf665b4d44dd186355827c0b049d2f95c8cbb45fd10d4e7cb255be67ecb?s=96&d=mm&r=g","caption":"Joan Frey"},"url":"https:\/\/www.dbi-services.com\/blog\/author\/joanfrey\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/43200","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=43200"}],"version-history":[{"count":25,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/43200\/revisions"}],"predecessor-version":[{"id":43250,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/43200\/revisions\/43250"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media\/43221"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=43200"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=43200"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=43200"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=43200"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}