A customer recently requested the integration of PostgreSQL with LDAP. I had successfully implemented similar solutions in previous projects using ldap2pg. However, this project was slightly different: PostgreSQL was deployed on Kubernetes using CloudNativePG (CNPG). This provided an interesting opportunity to combine LDAP synchronization with a modern Kubernetes-native PostgreSQL operator.
Prerequisites
I had no access to a Kubernetes cluster to test and play around, so I used Minikube on my Fedora Linux. The installation is basically a one-liner.
Openldap
First, we need a source for synchronizing roles into PostgreSQL. For this test, I chose OpenLDAP. I started by creating a namespace called ldap and switching the context to this namespace.
Since I had no reference implementation of OpenLDAP on Kubernetes at hand, I searched and found a Helm chart that looked simple enough to run OpenLDAP. The repository can be added quickly with the helm repo add command.
The next step took a little longer: I created an overrides.yaml file with a basic configuration for OpenLDAP and some custom data to synchronize with ldap2pg.
There are two groups, pgusers and pgadmins, and two users, dirk and slonik. User dirk is member of the pgadmins group and user slonik belongs to the pgusers group.
After several iterations – adding missing parameters and fixing syntax issues โ the configuration worked when installed with the helm upgrade --install command.
OpenLDAP is now up and running!
The next step was to validate the data that ldap2pg and PostgreSQL would later query. To do this, I connected to the OpenLDAP pod and opened a shell.
The container includes ldapsearch, which is a very handy tool for querying OpenLDAP. The key parameters in this case were:
-vvv: increases verbosity for detailed output-h openldap-0 -p 389: connects to the OpenLDAP pod on port 389-x: uses simple authentication-D 'cn=admin,dc=ldap,dc=demo,dc=lab': specifies the bind DN (here, the OpenLDAP admin account)-b 'cn=dirk,ou=users,dc=ldap,dc=demo,dc=lab': sets the base DN, in this case pointing directly to user dirk-s 'base': defines the search scope-w: prompts for the bind userโs password
The query returned the expected result.
The container includes ldapsearch, which is a very handy tool for querying OpenLDAP. The key parameters in this case were:
-vvv: increases verbosity for detailed output-h openldap-0 -p 389: connects to the OpenLDAP pod on port 389-x: uses simple authentication-D 'cn=admin,dc=ldap,dc=demo,dc=lab': specifies the bind DN (here, the OpenLDAP admin account)-b 'cn=dirk,ou=users,dc=ldap,dc=demo,dc=lab': sets the base DN, in this case pointing directly to user dirk-s 'base': defines the search scope-w: prompts for the bind userโs password
The query returned the expected result.
CloudNativePG
To simplify work with CNPG, I installed the CNPG Plugin for kubectl. The installation is just a one-liner.
Next, the CNPG operator was installed by applying the operator manifest.
With the operator ready, the next step was to create a new namespace pg and switch the context.
From there, I took the basic small-cluster template and added the following definitions:
- an
ldapsection containing connection details for OpenLDAP, essentially the same as used in theldapsearchtest - a custom
pg_hba.confentry for the user ldap2pg - a managed role
ldap2pgused by ldap2pg - a Kubernetes secret
ldap2pg-pwholding the password for the managed role
After applying the manifest, the PostgreSQL cluster cluster-demo was created and became available within a minute.
Connecting to PostgreSQL with psql showed the expected LDAP configuration in pg_hba.conf, but no LDAP-synced roles yet.
ldap2pg
It was time to deploy ldap2pg. This required a ConfigMap containing the ldap2pg.yml file, plus the necessary secrets for connecting to both OpenLDAP and PostgreSQL. ldap2pg would run periodically as a Kubernetes CronJob.
The secret ldapbindpw was created for the OpenLDAP admin password.
The ldap2pg.yml was kept deliberately simple: it defines two static roles (pgadmins and pgusers) and synchronizes users from LDAP groups into PostgreSQL with the LOGIN attribute, assigning them to their respective groups.
The CronJob configuration then defined how ldap2pg would run.
Note: Running ldap2pg without the
--realflag performs only a dry run, displaying planned changes without applying them. This is particularly useful while fine-tuning synchronization rules.
Authentication Example
With all components in place, I performed a final authentication test. I forwarded my local port 55432 to the PostgreSQL clusterโs read/write service on port 5432.
Using the LDAP user dirk, authentication with PostgreSQLโs psql CLI worked flawlessly.
Conclusion
This small project demonstrated how easily CloudNativePG can integrate with LDAP authentication, while ldap2pg provides the flexibility to synchronize LDAP groups and users directly into PostgreSQL. The combination offers a lightweight yet powerful solution for role and privilege management in Kubernetes-based PostgreSQL environments.
Although this article covered only the essentials – without addressing security hardening or enterprise-grade requirements – it establishes a solid foundation for more advanced setups. With CNPG and ldap2pg working together, organizations can align their PostgreSQL role management seamlessly with existing LDAP directory structures.

Dirk Aumueller
Dirk Aumueller works as an Associate Partner for Proventa AG. His technological focus is on database architectures with PostgreSQL as well as data management solutions using Pentaho. In addition to his database transformation project assignments, he regularly works as a PostgreSQL trainer and supervises students during their thesis projects. His professional experience spans the telecommunications and financial services industries.