<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 12ww1160</title>
    <description>The latest articles on DEV Community by 12ww1160 (@12ww1160).</description>
    <link>https://dev.to/12ww1160</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3668992%2F6029d290-e22d-4744-85cd-6a9d8bbbed92.jpeg</url>
      <title>DEV Community: 12ww1160</title>
      <link>https://dev.to/12ww1160</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/12ww1160"/>
    <language>en</language>
    <item>
      <title>CNPG - Configuration - TLS Encryption</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Mon, 06 Jul 2026 11:52:04 +0000</pubDate>
      <link>https://dev.to/12ww1160/cnpg-configuration-tls-encryption-4j1l</link>
      <guid>https://dev.to/12ww1160/cnpg-configuration-tls-encryption-4j1l</guid>
      <description>&lt;h2&gt;
  
  
  Using TLS Encryption with CloudNativePG (CNPG)
&lt;/h2&gt;

&lt;p&gt;Encryption has become essential in modern infrastructure. When working with databases, two main types of encryption come into play:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;At-rest encryption&lt;/strong&gt; — where the database files stored on disk are encrypted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-transit encryption (transport encryption)&lt;/strong&gt; — where the connection between the database server and clients is encrypted, protecting data from eavesdropping, spoofing, and man-in-the-middle attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post focuses exclusively on transport encryption in CloudNativePG.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encryption Modes in CloudNativePG
&lt;/h2&gt;

&lt;p&gt;The CloudNativePG operator supports two primary modes for managing TLS certificates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operator-managed mode — Certificates are fully handled and automatically renewed by the operator. They are signed by a dedicated Certificate Authority (CA) created by CloudNativePG.&lt;/li&gt;
&lt;li&gt;User-provided mode — You supply your own certificates (generated externally) and import them as Kubernetes Secrets. This mode also integrates smoothly with tools like cert-manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up TLS Encryption
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Operator-Managed Mode (Default)
&lt;/h3&gt;

&lt;p&gt;If you use the default operator-managed mode, no &lt;strong&gt;extra configuration is required&lt;/strong&gt;. CloudNativePG automatically enables TLS encryption on the server side.&lt;/p&gt;

&lt;p&gt;Here’s what happens behind the scenes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The operator creates a Kubernetes Secret named -ca containing the Certificate Authority certificate.&lt;/li&gt;
&lt;li&gt;It also creates a TLS Secret named -server with the server certificate and private key.&lt;/li&gt;
&lt;li&gt;These secrets are automatically mounted into the PostgreSQL pods.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To verify that TLS is active, connect to your database using &lt;code&gt;psql&lt;/code&gt; (or any other client) and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;conninfo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my_db=&amp;gt; \conninfo
            Connection Information
      Parameter       |         Value          
----------------------+------------------------
 Database             | my_db
 Client User          | my_db
 Host                 | 127.0.0.1
 Server Port          | 5432
 Options              | 
 Protocol Version     | 3.0
 Password Used        | true
 GSSAPI Authenticated | false
 Backend PID          | 1016607281
 SSL Connection       | true
 SSL Library          | OpenSSL
 SSL Protocol         | TLSv1.3
 SSL Key Bits         | 256
 SSL Cipher           | TLS_AES_256_GCM_SHA384
 SSL Compression      | false
 ALPN                 | postgresql
 Superuser            | off
 Hot Standby          | off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms the connection is encrypted with a strong TLSv1.3 cipher.&lt;br&gt;
For additional security, you can download the CA certificate from Kubernetes and configure your client to verify the server certificate using &lt;code&gt;sslmode=verify-ca&lt;/code&gt; or &lt;code&gt;sslmode=verify-full&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Replica connections are encrypted in exactly the same way. Certificate renewals happen seamlessly without any downtime.&lt;br&gt;
For most non-production and many production environments, this built-in approach is more than sufficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  User-Provided Certificates Mode
&lt;/h3&gt;

&lt;p&gt;When you need full control — for example, to use a company-wide internal CA — you can provide your own certificates.&lt;br&gt;
The official documentation explains the process in detail: &lt;a href="https://cloudnative-pg.io/docs/1.29/certificates/#user-provided-certificates-mode" rel="noopener noreferrer"&gt;User-provided certificates mode&lt;/a&gt;.&lt;br&gt;
In summary, you create the necessary Secrets and reference them in your Cluster resource. CloudNativePG integrates cleanly with cert-manager if you prefer automated certificate lifecycle management.&lt;/p&gt;

&lt;h2&gt;
  
  
  How TLS Flows in Kubernetes with CloudNativePG
&lt;/h2&gt;

&lt;p&gt;Here’s a visual overview of the TLS certificate flow in operator-managed mode:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3mie8esx0xa9puf0n9o0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3mie8esx0xa9puf0n9o0.png" alt="Mermaid diagram" width="784" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This diagram shows how the operator provisions the certificates as Kubernetes Secrets, mounts them into the database pods, and enables secure client connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Securing your CloudNativePG cluster with TLS is straightforward — often just a matter of using the defaults. Whether you stick with the operator-managed certificates or bring your own, CloudNativePG makes transport encryption simple, reliable, and production-ready.&lt;br&gt;
Have you tried TLS with CloudNativePG yet? Feel free to share your experience in the comments!&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pilot/" rel="noopener noreferrer"&gt;Databases - Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-gitea-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - Gitea and PGBouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - PGbouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-pilot/" rel="noopener noreferrer"&gt;CNPG - Cloud Native Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-migration-considerations/" rel="noopener noreferrer"&gt;CNPG - considerations for migrating databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation/" rel="noopener noreferrer"&gt;CNPG - installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-roles-and-dbs/" rel="noopener noreferrer"&gt;CNPG - Installation - Roles and Databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-db-import/" rel="noopener noreferrer"&gt;CNPG - Installation - Database Import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-pooling/" rel="noopener noreferrer"&gt;CNPG - Installation - Connection pooling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-monitoring/" rel="noopener noreferrer"&gt;CNPG - Installation - Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>cnpg</category>
      <category>encryption</category>
    </item>
    <item>
      <title>CNPG - Installation - Monitoring</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Thu, 02 Jul 2026 11:23:40 +0000</pubDate>
      <link>https://dev.to/12ww1160/cnpg-installation-monitoring-3bal</link>
      <guid>https://dev.to/12ww1160/cnpg-installation-monitoring-3bal</guid>
      <description>&lt;h2&gt;
  
  
  Monitoring CloudNativePG Clusters with the PLG Stack
&lt;/h2&gt;

&lt;p&gt;Monitoring your CloudNativePG (CNPG) clusters is essential. Without proper visibility, small issues can quickly escalate into major outages or performance bottlenecks that affect your entire application.&lt;br&gt;
Fortunately, CNPG makes monitoring straightforward. While many monitoring solutions exist, this post focuses on the popular &lt;strong&gt;PLG stack—Prometheus, Loki, and Grafana&lt;/strong&gt;—which integrates seamlessly with Kubernetes.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Is the PLG Stack?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prometheus: An open-source time-series database and monitoring system. It collects and stores metrics from your applications and infrastructure. Unlike traditional threshold-based tools, Prometheus gathers data points over time, enabling powerful querying and alerting.&lt;/li&gt;
&lt;li&gt;Loki: A lightweight, horizontally scalable log aggregation system inspired by Prometheus. It indexes metadata labels rather than the full content of logs, making it cost-effective and easy to operate.&lt;/li&gt;
&lt;li&gt;Grafana: A powerful open-source visualization platform. It connects to various data sources (including Prometheus and Loki) to create rich dashboards, charts, and alerts. Grafana itself does not store monitoring data—it simply displays it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these tools provide a complete observability solution: metrics, logs, and beautiful visualizations.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Tech Stack in This Environment
&lt;/h2&gt;

&lt;p&gt;In this setup, a dedicated Prometheus instance runs inside Kubernetes alongside Thanos for long-term storage and MinIO as a cost-effective object store. This approach also collects metrics from the underlying Linux nodes, not just Kubernetes resources.&lt;br&gt;
Grafana runs as a single instance integrated with Keycloak for authentication. Loki operates quietly in the background, feeding logs into Grafana.&lt;/p&gt;
&lt;h2&gt;
  
  
  How CNPG Monitoring Works
&lt;/h2&gt;

&lt;p&gt;The great news is that &lt;strong&gt;no changes are required inside CNPG&lt;/strong&gt; itself. Every CNPG pod automatically exposes metrics on port 9187. If you are using poolers, they expose metrics on port 9127.&lt;br&gt;
Prometheus discovers and scrapes these metrics using Kubernetes service discovery.&lt;br&gt;
Here is the recommended scrape configuration for Prometheus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cnpg-pods'&lt;/span&gt;
  &lt;span class="na"&gt;kubernetes_sd_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pod&lt;/span&gt;

  &lt;span class="na"&gt;relabel_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Keep only CNPG PostgreSQL pods&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_pod_label_cnpg_io_cluster&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep&lt;/span&gt;
      &lt;span class="na"&gt;regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.+&lt;/span&gt;

    &lt;span class="c1"&gt;# Keep only the metrics port&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_pod_container_port_name&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep&lt;/span&gt;
      &lt;span class="na"&gt;regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^metrics$&lt;/span&gt;

    &lt;span class="c1"&gt;# Add useful labels&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_namespace&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;target_label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;namespace&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_pod_label_cnpg_io_cluster&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;target_label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_pod_name&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;target_label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pod&lt;/span&gt;

    &lt;span class="c1"&gt;# Clean up instance label&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__address__&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;target_label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;instance&lt;/span&gt;
      &lt;span class="na"&gt;regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(.+):\d+'&lt;/span&gt;
      &lt;span class="na"&gt;replacement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$1'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cnpg-pods'&lt;/span&gt;
  &lt;span class="na"&gt;kubernetes_sd_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pod&lt;/span&gt;

  &lt;span class="na"&gt;relabel_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Keep only CNPG PostgreSQL pods&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_pod_label_cnpg_io_cluster&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep&lt;/span&gt;
      &lt;span class="na"&gt;regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.+&lt;/span&gt;

    &lt;span class="c1"&gt;# Keep only the metrics port&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_pod_container_port_name&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep&lt;/span&gt;
      &lt;span class="na"&gt;regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^metrics$&lt;/span&gt;

    &lt;span class="c1"&gt;# Add useful labels&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_namespace&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;target_label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;namespace&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_pod_label_cnpg_io_cluster&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;target_label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__meta_kubernetes_pod_name&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;target_label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pod&lt;/span&gt;

    &lt;span class="c1"&gt;# Clean up instance label&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;__address__&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;target_label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;instance&lt;/span&gt;
      &lt;span class="na"&gt;regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(.+):\d+'&lt;/span&gt;
      &lt;span class="na"&gt;replacement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Required Permissions (RBAC)
&lt;/h2&gt;

&lt;p&gt;For Prometheus to discover CNPG pods, it needs the right permissions. Create the following resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus-svc-acc&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus-cnpg-discovery&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nodes&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nodes/metrics&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;services&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;endpoints&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pods&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;watch"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extensions"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;networking.k8s.io"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ingresses&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;watch"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;nonResourceURLs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/metrics"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRoleBinding&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus-cnpg-discovery&lt;/span&gt;
&lt;span class="na"&gt;roleRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus-cnpg-discovery&lt;/span&gt;
&lt;span class="na"&gt;subjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus-svc-acc&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once these are applied, Prometheus should show your CNPG pods as healthy targets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualizing Data in Grafana
&lt;/h2&gt;

&lt;p&gt;Connect Grafana to your Prometheus instance as a data source. You can then import the official CloudNativePG dashboard using ID &lt;code&gt;20417&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the PLG Stack Works Together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm1wkvdpkbnayrq0td4k7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm1wkvdpkbnayrq0td4k7.png" alt="Mermaid diagram" width="512" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow Summary:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CNPG pods expose metrics and logs.&lt;/li&gt;
&lt;li&gt;Prometheus scrapes metrics.&lt;/li&gt;
&lt;li&gt;Loki collects and stores logs.&lt;/li&gt;
&lt;li&gt;Grafana queries both systems and presents everything in user-friendly dashboards.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This setup gives you comprehensive visibility into your database clusters with minimal ongoing maintenance.&lt;/p&gt;




&lt;p&gt;In the next post, we will explore backups and archiving strategies for CloudNativePG. Stay tuned!&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pilot/" rel="noopener noreferrer"&gt;Databases - Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-gitea-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - Gitea and PGBouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - PGbouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-pilot/" rel="noopener noreferrer"&gt;CNPG - Cloud Native Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-migration-considerations/" rel="noopener noreferrer"&gt;CNPG - considerations for migrating databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation/" rel="noopener noreferrer"&gt;CNPG - installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-roles-and-dbs/" rel="noopener noreferrer"&gt;CNPG - Installation - Roles and Databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-db-import/" rel="noopener noreferrer"&gt;CNPG - Installation - Database Import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-pooling/" rel="noopener noreferrer"&gt;CNPG - Installation - Connection pooling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-configuration-encryption/" rel="noopener noreferrer"&gt;CNPG - Configuration - TLS Encryption&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>cnpg</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>CNPG - Installation - Connection pooling</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Wed, 01 Jul 2026 11:58:51 +0000</pubDate>
      <link>https://dev.to/12ww1160/cnpg-installation-connection-pooling-4698</link>
      <guid>https://dev.to/12ww1160/cnpg-installation-connection-pooling-4698</guid>
      <description>&lt;h2&gt;
  
  
  Connection Pooling in CloudNativePG: Managing Postgres Connections Efficiently
&lt;/h2&gt;

&lt;p&gt;Connection management is a critical aspect of running PostgreSQL databases. Whether you are operating a single instance or a high-availability failover cluster, applications can quickly run into connection limits. When too many connections are open, new ones get rejected, leading to application errors and downtime.&lt;/p&gt;

&lt;p&gt;Effective connection management should happen on both the client and server sides. You can limit the number of connections your applications open, while the database server enforces its own maximums. This is important for two main reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An idle connection still consumes RAM and system resources.&lt;/li&gt;
&lt;li&gt;Each additional connection increases overall memory usage, which can strain your infrastructure as the number of connections grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where &lt;strong&gt;PgBouncer&lt;/strong&gt; shines. PgBouncer acts as a lightweight connection pooler that sits between your applications and PostgreSQL. It reuses and recycles connections according to your configuration, ensuring you never exceed defined limits while dramatically improving efficiency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb5be1zg794r317ke5p83.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb5be1zg794r317ke5p83.png" alt="Mermaid diagram" width="287" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  PgBouncer Pooling Modes
&lt;/h2&gt;

&lt;p&gt;PgBouncer supports three main pool modes, each suited to different application behaviors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Session Mode&lt;/strong&gt;: A connection is assigned to a client for the entire duration of its session (from login until logout). This mode is the most compatible with applications that rely on session-level state, temporary tables, or prepared statements. It provides the highest compatibility but uses connections less efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction Mode&lt;/strong&gt;: The connection is returned to the pool after each transaction completes. This mode offers a good balance between compatibility and resource efficiency. Most modern applications work well here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Statement Mode&lt;/strong&gt;: The connection is released back to the pool after every single SQL statement. This is the most efficient mode for resource usage but has the strictest compatibility requirements—applications cannot use transactions spanning multiple statements or rely on session state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing the right mode depends on your application's behavior. Some apps work best in session mode, while others perform well in transaction mode. &lt;a href="https://confdroid.com/2026/03/db-postgres-gitea-pgbouncer/" rel="noopener noreferrer"&gt;See my post about pgbouncer and gitea&lt;/a&gt; as example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Pooling with CloudNativePG
&lt;/h2&gt;

&lt;p&gt;CloudNativePG (CNPG) includes built-in support for PgBouncer through a dedicated &lt;code&gt;Pooler&lt;/code&gt; resource. The operator does not create poolers automatically—you must define and apply them yourself.&lt;br&gt;
Key points about CNPG poolers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can create multiple poolers for a single cluster.&lt;/li&gt;
&lt;li&gt;A pooler cannot serve multiple clusters.&lt;/li&gt;
&lt;li&gt;Poolers can target read-write (&lt;code&gt;rw&lt;/code&gt;) or read-only (&lt;code&gt;ro&lt;/code&gt;) traffic.&lt;/li&gt;
&lt;li&gt;Poolers can serve different modes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an example of a basic read-write pooler in session mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql.cnpg.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pooler&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pooler-example-rw&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-namespace&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster-example&lt;/span&gt;
  &lt;span class="na"&gt;instances&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rw&lt;/span&gt;
  &lt;span class="na"&gt;pgbouncer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;poolMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;session&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;max_client_conn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1000"&lt;/span&gt;
      &lt;span class="na"&gt;default_pool_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes on the configuration:&lt;/p&gt;

&lt;p&gt;The pooler name is your choice but cannot match the cluster name.&lt;br&gt;
It must be deployed in the same namespace as the cluster.&lt;br&gt;
Using suffixes like &lt;code&gt;-rw&lt;/code&gt; or &lt;code&gt;-ro&lt;/code&gt; helps when running separate read-write and read-only poolers, as well as describing the mode (&lt;code&gt;-session&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The read-write pooler attaches to the primary instance and automatically follows failovers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbad0epiqa2ddyjrs4wjx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbad0epiqa2ddyjrs4wjx.png" alt="cluster pooler rw" width="800" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;General Flow of PgBouncer:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applications connect to the PgBouncer service instead of the PostgreSQL cluster directly.&lt;/li&gt;
&lt;li&gt;PgBouncer accepts incoming client connections and manages a smaller pool of actual connections to PostgreSQL.&lt;/li&gt;
&lt;li&gt;When a client requests data, PgBouncer assigns (or reuses) a backend connection.&lt;/li&gt;
&lt;li&gt;After the operation (depending on the pool mode), the connection returns to the pool for reuse.&lt;/li&gt;
&lt;li&gt;This dramatically reduces the load on PostgreSQL while keeping resource usage predictable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Multiple Poolers Are Often Needed
&lt;/h2&gt;

&lt;p&gt;Not every application behaves well with the same pooling mode. For example, some tools work flawlessly in session mode but fail in transaction mode, while others show the opposite behavior. Since the pool mode is set at the pooler level, it is common to run multiple poolers—one for each required mode—and point different applications to the appropriate one.&lt;/p&gt;

&lt;h2&gt;
  
  
  External Access to Poolers
&lt;/h2&gt;

&lt;p&gt;For applications running outside the Kubernetes cluster, expose the poolers using NodePort or LoadBalancer services. Because external load balancers typically cannot route the same port to different backends, assign different ports to each pooler (e.g., 5432 for one and 6432 for another).&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Configuration Options
&lt;/h2&gt;

&lt;p&gt;You can fine-tune PgBouncer with many parameters. For a complete list, refer to the &lt;a href="https://cloudnative-pg.io/docs/1.29/connection_pooling#pgbouncer-configuration-options" rel="noopener noreferrer"&gt;official CNPG documentation on PgBouncer configuration options.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring and Observability
&lt;/h2&gt;

&lt;p&gt;CNPG’s PgBouncer pooler includes a built-in Prometheus exporter with metrics prefixed by &lt;code&gt;cnpg_pgbouncer_&lt;/code&gt;. Adding the proper scrape configuration to Prometheus allows you to collect these metrics and build insightful dashboards in Grafana.&lt;br&gt;
The official CloudNativePG Grafana dashboard works great with these metrics.&lt;br&gt;
Important reminder: Simply creating a pooler is not enough. You must update your applications to connect through the pooler service instead of the cluster service directly. Monitoring these connections and pool utilization is essential for performance tuning and ensuring everything runs smoothly.&lt;br&gt;
Connection pooling with PgBouncer in CloudNativePG is one of the most effective ways to keep your PostgreSQL clusters stable and performant under load. Taking the time to configure the right modes and monitor usage pays off significantly in reliability and resource efficiency.&lt;/p&gt;




&lt;p&gt;The next post in this series will look into monitoring CNPG, a very interesting topic.&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pilot/" rel="noopener noreferrer"&gt;Databases - Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-gitea-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - Gitea and PGBouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - PGbouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-pilot/" rel="noopener noreferrer"&gt;CNPG - Cloud Native Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-migration-considerations/" rel="noopener noreferrer"&gt;CNPG - considerations for migrating databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation/" rel="noopener noreferrer"&gt;CNPG - installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-roles-and-dbs/" rel="noopener noreferrer"&gt;CNPG - Installation - Roles and Databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-db-import/" rel="noopener noreferrer"&gt;CNPG - Installation - Database Import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-monitoring/" rel="noopener noreferrer"&gt;CNPG - Installation - Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-configuration-encryption/" rel="noopener noreferrer"&gt;CNPG - Configuration - TLS Encryption&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>cnpg</category>
      <category>gitops</category>
    </item>
    <item>
      <title>CNPG - Installation - Database Import</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Sat, 27 Jun 2026 14:05:20 +0000</pubDate>
      <link>https://dev.to/12ww1160/cnpg-installation-database-import-1no4</link>
      <guid>https://dev.to/12ww1160/cnpg-installation-database-import-1no4</guid>
      <description>&lt;h2&gt;
  
  
  Importing Databases into CloudNativePG (CNPG) Clusters
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://confdroid.com/db-cnpg-installation-roles-and-dbs/" rel="noopener noreferrer"&gt;previous post of our CNPG series&lt;/a&gt;, we explored the importance of secrets, roles, and database creation in a multi-database cluster setup. If you’ve been following along and applying the concepts, you should now have a working multi-node cluster with one or more empty databases ready to use.&lt;/p&gt;

&lt;p&gt;In this post, we’ll look at practical ways to bring your databases to life by importing real data. There are several approaches, each with its own strengths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Import Methods
&lt;/h2&gt;

&lt;p&gt;You generally have two main categories of database import:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native CNPG methods&lt;/strong&gt; (require access to the source cluster)

&lt;ul&gt;
&lt;li&gt;Microservice-style approach&lt;/li&gt;
&lt;li&gt;Monolithic-style approach&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External access methods&lt;/strong&gt; (using standard PostgreSQL tools)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these methods rely heavily on PostgreSQL’s native backup tools: &lt;code&gt;pg_dump&lt;/code&gt; and &lt;code&gt;pg_restore&lt;/code&gt;. The key differences lie in the workflow and requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  CNPG &lt;code&gt;initdb.import&lt;/code&gt; Method
&lt;/h3&gt;

&lt;p&gt;The official CloudNativePG documentation covers the &lt;code&gt;initdb.import&lt;/code&gt; feature in detail &lt;a href="https://cloudnative-pg.io/docs/1.29/database_import" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The process is straightforward when conditions are ideal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key requirements include:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full network connectivity between source and target clusters&lt;/li&gt;
&lt;li&gt;Proper access credentials&lt;/li&gt;
&lt;li&gt;Compatible TLS certificates (critical in production environments)&lt;/li&gt;
&lt;li&gt;Matching or newer PostgreSQL version on the target&lt;/li&gt;
&lt;li&gt;Reliable online backup strategy (including snapshots) on the source&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When everything aligns, this method can feel seamless. However, real-world migrations often face challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source clusters frequently run older PostgreSQL versions (sometimes as old as 9.x) while targets use modern versions (16–18).&lt;/li&gt;
&lt;li&gt;Older versions may lack advanced features like proper Multi-Version Concurrency Control (MVCC) snapshots.&lt;/li&gt;
&lt;li&gt;TLS certificate mismatches (different domains, intermediate CAs, etc.) are common.&lt;/li&gt;
&lt;li&gt;The source cluster might no longer be online or accessible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Careful planning and testing of workarounds are essential for successful migrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional PostgreSQL Export/Import (Most Common Approach)
&lt;/h3&gt;

&lt;p&gt;In many cases, standard &lt;code&gt;pg_dump&lt;/code&gt; backups already exist. These produce plain SQL files (e.g., &lt;code&gt;mydb_backup.sql&lt;/code&gt;) that can be easily transferred, reviewed, and restored.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works well for small to medium-sized databases&lt;/li&gt;
&lt;li&gt;Creates complete, consistent backups&lt;/li&gt;
&lt;li&gt;Easy to transfer over networks&lt;/li&gt;
&lt;li&gt;No complex TLS configuration needed between clusters&lt;/li&gt;
&lt;li&gt;Simple to automate with scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Potential security considerations during transfer&lt;/li&gt;
&lt;li&gt;Compliance with local data protection regulations&lt;/li&gt;
&lt;li&gt;Large databases may require physical media shipment&lt;/li&gt;
&lt;li&gt;Usually requires a maintenance window&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Typical Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s the standard process most teams follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Announce a maintenance window.&lt;/li&gt;
&lt;li&gt;Put the source database in read-only mode to prevent data loss.&lt;/li&gt;
&lt;li&gt;Create a full backup:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; postgres &lt;span class="nt"&gt;-h&lt;/span&gt; localhost mydb &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mydb_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Review and edit the backup file if needed (e.g., update database owners or references).&lt;/li&gt;
&lt;li&gt;Transfer the file to the target environment.&lt;/li&gt;
&lt;li&gt;Restore using one of these options:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Option A&lt;/strong&gt; – Using &lt;code&gt;pg_restore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_restore &lt;span class="nt"&gt;-d&lt;/span&gt; mydb mydb_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option B&lt;/strong&gt; – Using psql (often simplest for new empty databases):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-h&lt;/span&gt; newhost &lt;span class="nt"&gt;-U&lt;/span&gt; mydb &lt;span class="nt"&gt;-d&lt;/span&gt; mydb &amp;lt; mydb_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also pipe the dump directly (if network conditions allow):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; source_user &lt;span class="nt"&gt;-h&lt;/span&gt; source_host source_db | psql &lt;span class="nt"&gt;-U&lt;/span&gt; target_user &lt;span class="nt"&gt;-d&lt;/span&gt; target_db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Recommended Flow Diagram
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1t069e5un42fhg3f7w4b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1t069e5un42fhg3f7w4b.png" alt="Mermaid diagram" width="519" height="1408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Tips
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;In one recent project, I successfully migrated 18 databases of varying sizes by handling them one at a time. This approach allowed careful validation after each import. For larger-scale migrations, a well-tested shell script with looping can work well — but always expect at least one database to need special handling (different extensions, custom settings, etc.). Thorough testing in a staging environment is highly recommended.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;This method gives you a reliable, battle-tested way to move your databases into CloudNativePG. In future posts, we’ll dive deeper into post-import validation, performance tuning, and ongoing maintenance strategies.&lt;br&gt;
Stay tuned!&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pilot/" rel="noopener noreferrer"&gt;Databases - Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-gitea-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - Gitea and PGBouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - PGbouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-pilot/" rel="noopener noreferrer"&gt;CNPG - Cloud Native Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-migration-considerations/" rel="noopener noreferrer"&gt;CNPG - considerations for migrating databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation/" rel="noopener noreferrer"&gt;CNPG - installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-roles-and-dbs/" rel="noopener noreferrer"&gt;CNPG - Installation - Roles and Databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-pooling/" rel="noopener noreferrer"&gt;CNPG - Installation - Connection pooling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-monitoring/" rel="noopener noreferrer"&gt;CNPG - Installation - Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-configuration-encryption/" rel="noopener noreferrer"&gt;CNPG - Configuration - TLS Encryption&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>cnpg</category>
      <category>gitops</category>
    </item>
    <item>
      <title>CNPG - Installation - Roles and Databases</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Sat, 27 Jun 2026 10:42:57 +0000</pubDate>
      <link>https://dev.to/12ww1160/cnpg-installation-roles-and-databases-1anl</link>
      <guid>https://dev.to/12ww1160/cnpg-installation-roles-and-databases-1anl</guid>
      <description>&lt;h2&gt;
  
  
  Installing Your CNPG Cluster: Roles, Databases, and Access Control
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://confdroid.com/db-cnpg-installation/" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;, we walked through installing the CloudNativePG operator and bootstrapping a basic cluster. Now it is time to go deeper and configure roles, databases, and proper access controls—especially when running multiple databases within the same cluster.&lt;/p&gt;

&lt;p&gt;If you are only hosting a &lt;strong&gt;single database&lt;/strong&gt;, the basic bootstrap configuration from the last post is often all you need. It automatically creates the database, its owner, and a Kubernetes Secret for credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;initdb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;          &lt;span class="c1"&gt;# Name of your database&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;             &lt;span class="c1"&gt;# Database owner role&lt;/span&gt;
    &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-secret&lt;/span&gt;     &lt;span class="c1"&gt;# Kubernetes Secret for credentials&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose clear, descriptive names that match your application. For many environments, however, you will need multiple databases with isolated owners and stricter security rules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enabling Superuser Access
&lt;/h2&gt;

&lt;p&gt;Especially with multiple databases, it is highly recommended to enable superuser access on the cluster. This account is extremely useful for cross-database maintenance tasks, running custom backup scripts, performing cleanups, or executing administrative procedures.&lt;/p&gt;

&lt;p&gt;Add this to your &lt;code&gt;Cluster&lt;/code&gt; spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;instances&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
  &lt;span class="na"&gt;enableSuperuserAccess&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;   &lt;span class="c1"&gt;# Recommended,  this enables the superuser account&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The operator automatically creates a superuser and stores its credentials in a Secret named -superuser in the same namespace. You can retrieve the username and password from there whenever needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding Multiple Databases: Secrets, Roles, and pg_hba Rules
&lt;/h2&gt;

&lt;p&gt;For each additional database, you typically need three components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Kubernetes Secret containing the username and password.&lt;/li&gt;
&lt;li&gt;A managed Role defined in the Cluster.&lt;/li&gt;
&lt;li&gt;A Database custom resource to create the actual database.&lt;/li&gt;
&lt;li&gt;Optional but recommended: pg_hba rules for fine-grained connection control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a complete example for a database called &lt;code&gt;my-cool-app-db&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1. Kubernetes Secret (create this first)&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-cool-app-db-cluster-secret&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cnpg.io/reload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes.io/basic-auth&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;YWRtaW4=&lt;/span&gt;                      &lt;span class="c1"&gt;# base64 encoded "admin"&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;c3VwZXJzdHJvbmdwYXNzd29yZA==&lt;/span&gt;  &lt;span class="c1"&gt;# base64 encoded "password"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 2. Cluster definition with role and pg_hba&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql.cnpg.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cluster&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster-example-initdb&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;instances&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
  &lt;span class="na"&gt;enableSuperuserAccess&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;managed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-cool-app-role&lt;/span&gt;
        &lt;span class="na"&gt;ensure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;
        &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Role&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Cool-App&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;database"&lt;/span&gt;
        &lt;span class="na"&gt;passwordSecret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-cool-app-db-cluster-secret&lt;/span&gt;
        &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;                    &lt;span class="c1"&gt;# Important: allows login&lt;/span&gt;

  &lt;span class="na"&gt;postgresql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pg_hba&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;hostssl my-cool-app-db my-cool-app-role 10.244.0.0/16 md5&lt;/span&gt;

  &lt;span class="c1"&gt;# ... existing bootstrap and storage sections ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 3. Create the actual database&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql.cnpg.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Database&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-cool-app-db&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-cool-app-db&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-cool-app-role&lt;/span&gt;
  &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster-example-initdb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply everything with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; your-cluster.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or your preferred CI/CD tol, i.e. argo-cd.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: For demonstration reasons, the 3 sections above were all listed in the &lt;code&gt;cluster-config.yaml&lt;/code&gt; For better long-term maintainability, split Secrets, the main Cluster definition, and individual Database resources into separate YAML files. This makes it easier to manage and review changes over time.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How it all acts together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="/assets/cnpg_security.png" class="article-body-image-wrapper"&gt;&lt;img src="/assets/cnpg_security.png" alt="security - secrets, roles and access control through the CNPG operator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes Secrets store credentials securely.&lt;/li&gt;
&lt;li&gt;The CNPG Operator reads these Secrets and creates or updates PostgreSQL Roles (users) inside the database.&lt;/li&gt;
&lt;li&gt;You define pg_hba rules in the Cluster manifest to control who can connect, from where, and how (SSL, authentication method).&lt;/li&gt;
&lt;li&gt;Applications connect using the credentials from the Secrets, and only connections matching the pg_hba rules are allowed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup gives you strong isolation and security while keeping everything declarative and GitOps-friendly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use strong, unique passwords for each database role.&lt;/li&gt;
&lt;li&gt;Leverage pg_hba rules to restrict connections by database, user, and network source.&lt;/li&gt;
&lt;li&gt;Keep Secrets in Git (encrypted if necessary) or manage them through your secret management solution.&lt;/li&gt;
&lt;li&gt;Test access thoroughly after applying changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With roles and databases properly configured, your CNPG cluster is now ready for real data.&lt;/p&gt;




&lt;p&gt;In the next post, we will cover importing existing database content into your new CNPG cluster—using logical replication, pg_dump/restore, and other proven strategies.&lt;br&gt;
Have you started building your CNPG clusters? What challenges have you run into with roles and multi-database setups? Share your experiences in the comments.&lt;br&gt;
Stay tuned for the next installment in the CNPG series.&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pilot/" rel="noopener noreferrer"&gt;Databases - Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-gitea-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - Gitea and PGBouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - PGbouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-pilot/" rel="noopener noreferrer"&gt;CNPG - Cloud Native Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-migration-considerations/" rel="noopener noreferrer"&gt;CNPG - considerations for migrating databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation/" rel="noopener noreferrer"&gt;CNPG - installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-db-import/" rel="noopener noreferrer"&gt;CNPG - Installation - Database Import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-pooling/" rel="noopener noreferrer"&gt;CNPG - Installation - Connection pooling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-monitoring/" rel="noopener noreferrer"&gt;CNPG - Installation - Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-configuration-encryption/" rel="noopener noreferrer"&gt;CNPG - Configuration - TLS Encryption&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>cnpg</category>
      <category>gitops</category>
    </item>
    <item>
      <title>CNPG - installation</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Thu, 25 Jun 2026 14:37:43 +0000</pubDate>
      <link>https://dev.to/12ww1160/cnpg-installation-3676</link>
      <guid>https://dev.to/12ww1160/cnpg-installation-3676</guid>
      <description>&lt;h2&gt;
  
  
  Installing CloudNativePG (CNPG): Step-by-Step Guide
&lt;/h2&gt;

&lt;p&gt;In previous posts, we explored the &lt;a href="https://confdroid.com/db-cnpg-pilot/" rel="noopener noreferrer"&gt;reasons for adopting CloudNativePG&lt;/a&gt; and the &lt;a href="https://confdroid.com/db-cnpg-migration-considerations/" rel="noopener noreferrer"&gt;key considerations for migrating databases&lt;/a&gt; to it. Now it is time to get practical and walk through the installation process.&lt;br&gt;
This guide assumes you are working in a GitOps-driven environment. All configuration lives in Git repositories, and deployments are handled through CI/CD pipelines ( i.e- GitLab + Jenkins for development, or GitLab + Argo CD for production). This approach ensures everything is version-controlled, repeatable, and auditable.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Install the CNPG Operator
&lt;/h2&gt;

&lt;p&gt;CNPG is a Kubernetes operator. Once installed, it watches for custom resources (mainly &lt;code&gt;Cluster&lt;/code&gt; objects) and manages the full lifecycle of your PostgreSQL databases.&lt;br&gt;
The official way to install the operator is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;--server-side&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.29/releases/cnpg-1.29.1.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Recommended GitOps practice:&lt;/strong&gt;&lt;br&gt;
Instead of applying the manifest directly from the internet, download it into your control repository. This gives you a stable, pinned version that you can reapply anytime.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the YAML file to your Git repo.&lt;/li&gt;
&lt;li&gt;For environments using Jenkins, create a pipeline stage that runs:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;--server-side&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /path/to/cnpg-1.29.1.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Argo CD may need special handling for the &lt;code&gt;--server-side&lt;/code&gt; flag, so Jenkins often handles the initial operator install.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can monitor the rollout with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout status deployment &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; cnpg-system cnpg-controller-manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or simply watch the deployment in a Kubernetes dashboard such as &lt;a href="https://headlamp.dev/" rel="noopener noreferrer"&gt;Headlamp&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Create a ClusterImageCatalog
&lt;/h2&gt;

&lt;p&gt;It is highly recommended to define the PostgreSQL images you want to use. This gives you control over major versions and prevents unexpected upgrades.&lt;/p&gt;

&lt;p&gt;Create and apply the following manifest in your repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql.cnpg.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterImageCatalog&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql-global&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;major&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/cloudnative-pg/postgresql:15.14-system-trixie&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;major&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/cloudnative-pg/postgresql:16.10-system-trixie&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;major&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;17&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/cloudnative-pg/postgresql:17.6-system-trixie&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;major&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/cloudnative-pg/postgresql:18.3-system-trixie&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the operator and image catalog in place, you are ready to define your actual database clusters.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Design Decisions Before Bootstrapping
&lt;/h2&gt;

&lt;p&gt;Before writing your first Cluster manifest, take time to plan the following aspects (many were covered in the migration considerations post):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Namespace — Use a dedicated namespace for each CNPG setup (e.g., postgres-prod). This improves isolation, especially when running multiple clusters.&lt;/li&gt;
&lt;li&gt;One cluster or many? — CNPG works best with one database per cluster for strong isolation. If your databases are small and have light workloads, you can host several in one cluster (as I did successfully).&lt;/li&gt;
&lt;li&gt;Storage backend — Choose the right Kubernetes StorageClass based on performance needs (SSD, NVMe, local volumes, etc.). Plan for both data and WAL storage.&lt;/li&gt;
&lt;li&gt;Superuser access — Not strictly required for single-database clusters, but very useful for backups, restores, and maintenance. Use separate roles and secrets for better isolation when hosting multiple databases.&lt;/li&gt;
&lt;li&gt;Connection pooling — Enable PgBouncer (integrated with CNPG) to handle connection management efficiently. It automatically follows failovers.&lt;/li&gt;
&lt;li&gt;WAL storage — Strongly consider a dedicated volume for the Write-Ahead Log. WAL can grow quickly and should not compete with data storage.&lt;/li&gt;
&lt;li&gt;Backup strategy — Plan for Barman (integrated object storage backups, often with MinIO on slower storage) plus optional traditional pg_dump exports.&lt;/li&gt;
&lt;li&gt;Data import method — Decide whether to use live logical replication from the source or bootstrap with existing backups.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Example Cluster Manifest
&lt;/h3&gt;

&lt;p&gt;Here is a minimal starting point for a new cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Create a dedicated namespace&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Namespace&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql.cnpg.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cluster&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster-example-initdb&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;instances&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

  &lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;initdb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-secret&lt;/span&gt;

  &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1Gi&lt;/span&gt;
    &lt;span class="c1"&gt;# storageClass: your-preferred-class&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This basic manifest creates a 3-instance highly available cluster. You will expand it with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Additional roles and databases&lt;/li&gt;
&lt;li&gt;Custom pg_hba rules&lt;/li&gt;
&lt;li&gt;Service exposure settings&lt;/li&gt;
&lt;li&gt;WAL storage configuration&lt;/li&gt;
&lt;li&gt;Barman object store for backups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apply the manifest with &lt;code&gt;kubectl apply -f your-cluster.yaml&lt;/code&gt;. The operator will spin up the cluster within a few minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next Steps and Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test everything first in a development environment using iterative kubectl apply.&lt;/li&gt;
&lt;li&gt;Once the configuration is solid, let Argo CD manage it in production.&lt;/li&gt;
&lt;li&gt;Plan your data import strategy carefully. The official bootstrap documentation is helpful but can feel dense on first read — hands-on testing is the best teacher.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will follow up with more detailed posts covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced cluster configuration&lt;/li&gt;
&lt;li&gt;Backup and disaster recovery setup&lt;/li&gt;
&lt;li&gt;Data migration techniques&lt;/li&gt;
&lt;li&gt;PgBouncer integration&lt;/li&gt;
&lt;li&gt;Monitoring and observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A sample chart gallery with ready-to-use manifests is also on the roadmap.&lt;/p&gt;




&lt;p&gt;Ready to deploy?&lt;br&gt;
Start small, document your decisions, and iterate. CloudNativePG makes PostgreSQL on Kubernetes reliable and pleasant to operate once the foundation is set correctly.&lt;br&gt;
Have you installed CNPG yet? What challenges did you face during setup? Feel free to share in the comments.&lt;br&gt;
Stay tuned for the next post in the CNPG series.&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pilot/" rel="noopener noreferrer"&gt;Databases - Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-gitea-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - Gitea and PGBouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - PGbouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-pilot/" rel="noopener noreferrer"&gt;CNPG - Cloud Native Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-migration-considerations/" rel="noopener noreferrer"&gt;CNPG - considerations for migrating databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-roles-and-dbs/" rel="noopener noreferrer"&gt;CNPG - Installation - Roles and Databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-db-import/" rel="noopener noreferrer"&gt;CNPG - Installation - Database Import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-pooling/" rel="noopener noreferrer"&gt;CNPG - Installation - Connection pooling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-monitoring/" rel="noopener noreferrer"&gt;CNPG - Installation - Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-configuration-encryption/" rel="noopener noreferrer"&gt;CNPG - Configuration - TLS Encryption&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>cnpg</category>
      <category>gitops</category>
    </item>
    <item>
      <title>CNPG - considerations for migrating databases</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Wed, 24 Jun 2026 12:27:34 +0000</pubDate>
      <link>https://dev.to/12ww1160/cnpg-considerations-for-migrating-databases-1g50</link>
      <guid>https://dev.to/12ww1160/cnpg-considerations-for-migrating-databases-1g50</guid>
      <description>&lt;h2&gt;
  
  
  Key Considerations for Migrating Databases to CloudNativePG (CNPG)
&lt;/h2&gt;

&lt;p&gt;Migrating databases is rarely simple. Achieving zero or minimal downtime while avoiding data loss demands careful planning. Moving to CloudNativePG (CNPG)—the Kubernetes-native PostgreSQL operator—introduces additional factors tied to containerized environments, Kubernetes storage, and operator-specific features.&lt;br&gt;
While no guide can cover every scenario, the following considerations provide a strong starting point for a smooth transition.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Database Size
&lt;/h2&gt;

&lt;p&gt;PostgreSQL databases consist of interconnected objects—schemas, tables, indexes, roles, and more—stored in a specific file structure managed by the server. Data movement typically relies on SQL operations, with backups (via pg_dump) being a common method. CNPG provides other methods, including direct access to the migration source, but that is not always feasible.&lt;/p&gt;

&lt;p&gt;Small databases (a few MB or GB) are straightforward to transfer. Large ones—think tens or hundreds of terabytes—require much more planning around storage, time, and bandwidth.&lt;/p&gt;

&lt;p&gt;Actionable first step:&lt;br&gt;
Query your current PostgreSQL server to understand the footprint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;datname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_database_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datname&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_database&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;pg_database_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns a clear, human-readable table of database sizes. Note that pg_dump files are usually compressed, so they may be smaller than the live database. Always provision extra headroom on the target side to accommodate growth.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pro tip: Before migrating, run cleanup jobs on the source. Remove old, unused data, temporary tables, or orphaned records. Every DBA knows how clutter accumulates—there is no reason to carry it forward.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. Database Type (Source Compatibility)
&lt;/h2&gt;

&lt;p&gt;Your source may not be PostgreSQL. Many applications support multiple backends, such as MySQL, MariaDB, or others. In these cases, you will need schema conversion, data type mapping, and possibly query adjustments.&lt;/p&gt;

&lt;p&gt;Popular tools include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pgloader — Excellent for migrating from MySQL or other systems to PostgreSQL.&lt;/li&gt;
&lt;li&gt;mysqldump with compatibility flags, followed by manual tweaks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plan for differences in features, stored procedures, and data types early in the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Query and I/O Patterns
&lt;/h2&gt;

&lt;p&gt;Size alone does not tell the full story. A modest database under heavy load can stress CPU, memory, storage IOPS, and network resources more than a large but quiet one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Profile current workloads using tools like pg_stat_statements, pg_stat_io (available in PostgreSQL 16+), or external monitoring.&lt;/li&gt;
&lt;li&gt;Benchmark the expected load on the target Kubernetes environment.&lt;/li&gt;
&lt;li&gt;Leverage CNPG’s built-in replication: add multiple read-only replicas (typically three or more instances per cluster) to offload read traffic from the primary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding IOPS and file system behavior versus actual PostgreSQL activity is complex and often warrants dedicated testing. A well-provisioned CNPG cluster with proper storage classes can handle demanding workloads effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. High Availability (HA) Requirements
&lt;/h2&gt;

&lt;p&gt;Traditional PostgreSQL HA solutions (e.g., Patroni) work well but require manual management. CNPG provides native, automated HA with replicas and failover capabilities, plus integrated PgBouncer support for connection pooling.&lt;/p&gt;

&lt;p&gt;This setup suits most applications using a primary for writes and replicas for reads. However, it is not an active-active configuration with multiple writable nodes. If your application demands true multi-master writes, conduct deeper research or consider additional patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Backup and Restore Strategies
&lt;/h2&gt;

&lt;p&gt;Backups are only as good as your last successful restore. Many organizations discover gaps only during a crisis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate existing backup procedures and adapt them for CNPG.&lt;/li&gt;
&lt;li&gt;CNPG offers built-in support for continuous archiving and recovery, often using object storage like S3.&lt;/li&gt;
&lt;li&gt;For smaller databases or extra safety, combine this with traditional &lt;code&gt;pg_dump&lt;/code&gt; / &lt;code&gt;pg_restore&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Test restores regularly—ideally onto new clusters—to confirm end-to-end recoverability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Data Transport
&lt;/h2&gt;

&lt;p&gt;Moving data from on-premises to cloud (or between data centers) introduces logistical challenges.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Network constraints&lt;/strong&gt;: Private networks, firewalls, and bandwidth limits can complicate large transfers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physical media&lt;/strong&gt;: For very large databases, shipping drives is sometimes necessary, but many data centers restrict external USB/storage devices due to security policies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alternatives&lt;/strong&gt;: Logical replication (via CNPG’s native PostgreSQL support) enables near-zero-downtime migrations by streaming changes in real time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plan the transport method early and account for security, compliance, and downtime windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Application Connectivity
&lt;/h2&gt;

&lt;p&gt;Migration affects more than just the database. Consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whether the application moves with the database or connects remotely.&lt;/li&gt;
&lt;li&gt;Updated connection strings, service discovery in Kubernetes, and network policies.&lt;/li&gt;
&lt;li&gt;New RBAC roles, secrets management, and security configurations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test connectivity thoroughly in a staging environment, paying special attention to Kubernetes networking and service exposure.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. One Cluster or Many? (CNPG Architecture Decisions)
&lt;/h2&gt;

&lt;p&gt;CNPG strongly recommends &lt;strong&gt;one PostgreSQL cluster per database&lt;/strong&gt; (or per microservice) for isolation, security, and operational simplicity.&lt;br&gt;
&lt;strong&gt;Important storage consideration&lt;/strong&gt;: Decide whether to place Write-Ahead Log (WAL) files on the same volume as data or on a dedicated volume. WAL can grow rapidly, especially under heavy load or during replication lag. Using a separate &lt;code&gt;walStorage&lt;/code&gt; volume prevents one from starving the other.&lt;/p&gt;

&lt;p&gt;Evaluate your disk space, I/O patterns, and resource availability. Some teams run many databases in a single large CNPG cluster successfully; others prefer multiple smaller clusters on dedicated nodes. Choose based on validated requirements rather than convenience alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Migrating to CloudNativePG offers powerful Kubernetes-native PostgreSQL capabilities—automated HA, declarative management, and excellent operator integration—but success depends on thoughtful preparation.&lt;br&gt;
Start with assessment (size, load, compatibility), design for isolation and performance, validate backups and connectivity, and test thoroughly. Tools like logical replication can minimize disruption, while proper storage configuration prevents nasty surprises with WAL growth.&lt;br&gt;
With careful planning, you can achieve a reliable, scalable PostgreSQL environment that leverages the full power of Kubernetes. Good luck with your migration!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have you migrated to CNPG? Share your experiences or specific challenges in the comments.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pilot/" rel="noopener noreferrer"&gt;Databases - Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-gitea-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - Gitea and PGBouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - PGbouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-pilot/" rel="noopener noreferrer"&gt;CNPG - Cloud Native Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation/" rel="noopener noreferrer"&gt;CNPG - installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-roles-and-dbs/" rel="noopener noreferrer"&gt;CNPG - Installation - Roles and Databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-db-import/" rel="noopener noreferrer"&gt;CNPG - Installation - Database Import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-pooling/" rel="noopener noreferrer"&gt;CNPG - Installation - Connection pooling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-monitoring/" rel="noopener noreferrer"&gt;CNPG - Installation - Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-configuration-encryption/" rel="noopener noreferrer"&gt;CNPG - Configuration - TLS Encryption&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>cnpg</category>
      <category>gitops</category>
    </item>
    <item>
      <title>CNPG - Cloud Native Postgresql - Pilot</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Sun, 14 Jun 2026 13:26:22 +0000</pubDate>
      <link>https://dev.to/12ww1160/cnpg-cloud-native-postgresql-pilot-5gde</link>
      <guid>https://dev.to/12ww1160/cnpg-cloud-native-postgresql-pilot-5gde</guid>
      <description>&lt;h2&gt;
  
  
  Moving from Traditional Postgres to CNPG (Cloud Native Postgres)
&lt;/h2&gt;

&lt;p&gt;Rising hosting costs and the desire to stay current with modern technologies prompted a significant shift in my lab cloud setup. The main goal was to reduce expenses while exploring newer tools. A key part of this plan involved moving as much workload as possible from traditional virtual machines to my existing, well-running Kubernetes cluster, which already hosts around 18 applications with plenty of capacity to spare.&lt;/p&gt;

&lt;h2&gt;
  
  
  Immediate Priorities
&lt;/h2&gt;

&lt;p&gt;Two quick wins stood out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retire the dedicated database server&lt;/li&gt;
&lt;li&gt;Move the Puppet-managed Nagios server onto an existing web server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking further ahead, I plan to migrate web services to Kubernetes, replace Nagios with Zabbix (which also runs nicely in Kubernetes), switch from Jenkins to GitLab CI, and give Prometheus another try in the cluster.&lt;/p&gt;

&lt;p&gt;Thanks to the growing ecosystem of Kubernetes operators, managing complex services has become much smoother. The end result should be roughly half the number of servers—and significantly lower costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  First Step: Moving to CNPG
&lt;/h2&gt;

&lt;p&gt;Last year, I experimented with a standard Postgres container, but the results were disappointing. Performance lagged behind my VM-based setup, and I was limited to a single instance. In contrast, the VM environment delivered solid performance and allowed near-complete management through &lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_postgresql" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The new objectives for the CNPG migration were clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run Postgres natively in Kubernetes&lt;/li&gt;
&lt;li&gt;Manage everything through CI/CD and GitOps&lt;/li&gt;
&lt;li&gt;Deploy a 3-node cluster&lt;/li&gt;
&lt;li&gt;Enable straightforward database migrations&lt;/li&gt;
&lt;li&gt;Implement reliable backups&lt;/li&gt;
&lt;li&gt;Improve internal transport security via the Kubernetes network (even though everything was already TLS-secured in a private network)&lt;/li&gt;
&lt;li&gt;Optionally expose database services to external applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This migration took several days and required careful attention to several key areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installation procedures&lt;/li&gt;
&lt;li&gt;Networking requirements&lt;/li&gt;
&lt;li&gt;Storage configuration&lt;/li&gt;
&lt;li&gt;Backup strategies&lt;/li&gt;
&lt;li&gt;Application connectivity&lt;/li&gt;
&lt;li&gt;Data migration approaches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This blog series will explore each of these topics in dedicated posts. I know many others are tackling similar projects at work or in their own environments, so I hope these guides prove helpful.&lt;br&gt;
Stay tuned for the first deep dive into the CNPG installation and setup process!&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pilot/" rel="noopener noreferrer"&gt;Databases - Postgresql - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-gitea-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - Gitea and PGBouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-postgres-pgbouncer/" rel="noopener noreferrer"&gt;Databases - Postgresql - PGbouncer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-migration-considerations/" rel="noopener noreferrer"&gt;CNPG - considerations for migrating databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation/" rel="noopener noreferrer"&gt;CNPG - installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-roles-and-dbs/" rel="noopener noreferrer"&gt;CNPG - Installation - Roles and Databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-db-import/" rel="noopener noreferrer"&gt;CNPG - Installation - Database Import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-pooling/" rel="noopener noreferrer"&gt;CNPG - Installation - Connection pooling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-installation-monitoring/" rel="noopener noreferrer"&gt;CNPG - Installation - Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/db-cnpg-configuration-encryption/" rel="noopener noreferrer"&gt;CNPG - Configuration - TLS Encryption&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>cnpg</category>
      <category>gitops</category>
    </item>
    <item>
      <title>ConfDroid Puppet Modules - java</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Sat, 18 Apr 2026 16:24:20 +0000</pubDate>
      <link>https://dev.to/12ww1160/confdroid-puppet-modules-java-5abd</link>
      <guid>https://dev.to/12ww1160/confdroid-puppet-modules-java-5abd</guid>
      <description>&lt;h2&gt;
  
  
  Introducing confdroid_java: A Lightweight Helper for Reliable Java Installations
&lt;/h2&gt;

&lt;p&gt;We’re continuing the ConfDroid Puppet modules series with a small but essential addition: &lt;strong&gt;confdroid_java&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This module does exactly one thing—and does it well. It installs and configures a specified Java version so that other Puppet modules can simply rely on it being there. No more guessing, no more manual setup, and no more version conflicts when you spin up Java-based services.&lt;/p&gt;

&lt;p&gt;Think of it as the Java counterpart to our popular &lt;strong&gt;confdroid_php&lt;/strong&gt; module. Just as &lt;code&gt;confdroid_php&lt;/code&gt; prepares a clean PHP environment for applications like WordPress or Nagios, confdroid_java sets up the exact Java runtime that tools like &lt;code&gt;confdroid_jenkins&lt;/code&gt; (and any future Java-dependent modules) expect. It keeps everything consistent, reproducible, and ready for automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What confdroid_java actually does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Installs the Java binaries you specify through parameters&lt;/li&gt;
&lt;li&gt;Places all necessary configuration files in the correct locations&lt;/li&gt;
&lt;li&gt;Automatically applies the right SELinux contexts (and gracefully ignores them on systems where SELinux is disabled)&lt;/li&gt;
&lt;li&gt;Stays deliberately lightweight—no unnecessary features, no external module dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is designed to be a silent foundation. You include it once, and every Java-needing module that follows can simply assume a working Java environment is already in place. Confdroid modules which rely on it like &lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_jenkins" rel="noopener noreferrer"&gt;confdroid_jenkins&lt;/a&gt; automatically add it, as long as it is in the Puppet catalogue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supported platforms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fully tested and validated on Rocky Linux 9&lt;/li&gt;
&lt;li&gt;Expected to work on all RHEL 9-based distributions&lt;/li&gt;
&lt;li&gt;Requires Puppet 8&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;The simplest way is just to include the class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;node&lt;/span&gt; &lt;span class="s1"&gt;'example.example.net'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="nc"&gt;confdroid_java&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you manage your nodes with Foreman, simply set the desired parameters (Java version, installation source, etc.) directly in the host or host-group configuration. No extra code required.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important note: This module is meant for fresh systems. If Java has already been installed or configured manually on a server, applying confdroid_java could overwrite those changes. Always test in a lab environment first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where to get it
&lt;/h2&gt;

&lt;p&gt;The full module, complete documentation, and parameter reference are available right now in the &lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_java" rel="noopener noreferrer"&gt;Confdroid Forge:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll also find the generated documentation in the &lt;code&gt;doc/&lt;/code&gt; folder inside the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this module matters
&lt;/h2&gt;

&lt;p&gt;In larger Puppet environments, helper modules like confdroid_java (and its PHP sibling) are the glue that makes everything else click. They remove duplication, enforce consistency, and let the bigger application modules focus on what they do best—managing Jenkins, application servers, or any other Java workload—without reinventing the Java installation wheel every time.&lt;br&gt;
If you’re already running other ConfDroid modules on Rocky 9, adding confdroid_java is a quick win that will make your Java-based services even more reliable and easier to maintain.&lt;br&gt;
Happy automating!&lt;br&gt;
As always, feedback and pull requests are welcome in the repository.&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-pilot/" rel="noopener noreferrer"&gt;Confdroid Puppet Modules - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-puppet/" rel="noopener noreferrer"&gt;Confdroid Puppet Modules - Puppet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-resources/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - confdroid_resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-postgresql/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Postgresql&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-gitea/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Gitea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-apache/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-nagios/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Nagios&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-nrpe/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - NRPE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-fail2ban/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Fail2ban&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-automatic/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Automatic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-selinux/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Selinux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-ssh/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - SSH&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>puppet</category>
      <category>foreman</category>
    </item>
    <item>
      <title>ConfDroid Puppet Modules - SSH</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Sat, 18 Apr 2026 15:33:33 +0000</pubDate>
      <link>https://dev.to/12ww1160/confdroid-puppet-modules-ssh-1kke</link>
      <guid>https://dev.to/12ww1160/confdroid-puppet-modules-ssh-1kke</guid>
      <description>&lt;h2&gt;
  
  
  Introducing confdroid_ssh: Reliable and Hardened SSH Access for Your Rocky 9 Servers
&lt;/h2&gt;

&lt;p&gt;SSH is the primary way we access and manage Linux servers. When SSH stops working, everything else grinds to a halt — troubleshooting becomes painful, and automation pipelines can fail.&lt;/p&gt;

&lt;p&gt;To solve this, I created &lt;strong&gt;confdroid_ssh&lt;/strong&gt;, a new Puppet 8 module that ensures the SSH daemon (sshd) is always installed, properly configured, running, and reachable.&lt;/p&gt;

&lt;p&gt;The module provides a hardened, consistent SSH setup across your entire infrastructure while making it easy to apply custom security policies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Module Matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Guarantees SSH access is always available.&lt;/li&gt;
&lt;li&gt;Applies secure defaults for ciphers, MAC algorithms, and other important settings&lt;/li&gt;
&lt;li&gt;Manages the main sshd_config safely through drop-in files in /etc/ssh/sshd_config.d/&lt;/li&gt;
&lt;li&gt;Handles SELinux contexts automatically (works great together with confdroid_selinux)&lt;/li&gt;
&lt;li&gt;Optionally manages firewall rules to keep the SSH port open&lt;/li&gt;
&lt;li&gt;Prevents configuration drift and manual overrides that often cause problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It fits perfectly into the Confdroid collection alongside &lt;code&gt;confdroid_selinux&lt;/code&gt; (for global SELinux enforcement) and &lt;code&gt;confdroid_fail2ban&lt;/code&gt; (for brute-force protection on SSH).&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Installs the SSH server packages and required binaries&lt;/li&gt;
&lt;li&gt;Manages the sshd service (ensures it is enabled and running)&lt;/li&gt;
&lt;li&gt;Supports custom configuration snippets via the confdroid_ssh::custom::custom_config define&lt;/li&gt;
&lt;li&gt;Automatically applies correct SELinux contexts&lt;/li&gt;
&lt;li&gt;Optionally opens the SSH port in the firewall (iptables/nftables)&lt;/li&gt;
&lt;li&gt;Designed for Rocky 9 (and other RHEL 9-based systems)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example: Adding Custom Configuration
&lt;/h2&gt;

&lt;p&gt;You can easily add your own secure settings without touching the main config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="n"&gt;confdroid_ssh::custom::custom_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'30-my-hardening'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;config_name&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'30-my-hardening'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;config_content&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'PasswordAuthentication no'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'PermitRootLogin no'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'MaxAuthTries 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a cleanly managed file /etc/ssh/sshd_config.d/30-my-hardening.conf, overriding default settings from the main configuration file.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Fits in the Confdroid Ecosystem
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;confdroid_ssh&lt;/code&gt; works hand-in-hand with the rest of the collection:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;confdroid_selinux&lt;/code&gt; ensures the global SELinux mode is set correctly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;confdroid_fail2ban&lt;/code&gt; protects SSH against brute-force attacks
All other modules benefit from reliable SSH access for management and deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Important Notes
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: This module overwrites SSH configuration. Do not use it right on systems that have heavy manual SSH customizations. Always test first in a non-production environment, and move your manual configuration into the module via the provided define see example. It is likely best practice to use small snippets depending on various conditions &lt;strong&gt;only when&lt;/strong&gt; they apply. Not every Linux system follows the same pattern depending on the applications it hosts.&lt;br&gt;
The module follows the Confdroid “ENC-first” philosophy — configure everything comfortably through Foreman smart class parameters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can find the full module, source code, parameter reference, and documentation &lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_ssh" rel="noopener noreferrer"&gt;here: https://sourcecode.confdroid.com/confdroid/confdroid_ssh&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;confdroid_ssh&lt;/code&gt;, you no longer have to worry about SSH breaking after updates or configuration changes. It provides a solid, hardened, and fully automated foundation for secure remote access across your Rocky 9 fleet.&lt;/p&gt;

&lt;p&gt;Combined with &lt;code&gt;confdroid_selinux&lt;/code&gt; and &lt;code&gt;confdroid_fail2ban&lt;/code&gt;, it forms a strong security layer that keeps your servers accessible to you — but not to attackers.&lt;br&gt;
Have you ever lost SSH access due to a misconfiguration or update? How do you currently manage SSH hardening across your servers? Share your experiences or questions in the comments — I’d love to hear them!&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-pilot/" rel="noopener noreferrer"&gt;Confdroid Puppet Modules - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-puppet/" rel="noopener noreferrer"&gt;Confdroid Puppet Modules - Puppet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-resources/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - confdroid_resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-postgresql/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Postgresql&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-gitea/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Gitea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-apache/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-nagios/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Nagios&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-nrpe/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - NRPE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-fail2ban/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Fail2ban&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-automatic/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Automatic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-selinux/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Selinux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-java/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sshd</category>
      <category>access</category>
      <category>puppet</category>
      <category>foreman</category>
    </item>
    <item>
      <title>ConfDroid Puppet Modules - Selinux</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Sat, 11 Apr 2026 10:04:51 +0000</pubDate>
      <link>https://dev.to/12ww1160/confdroid-puppet-modules-selinux-4im3</link>
      <guid>https://dev.to/12ww1160/confdroid-puppet-modules-selinux-4im3</guid>
      <description>&lt;h2&gt;
  
  
  Introducing confdroid_selinux: Declarative SELinux Management for Your Rocky 9 Servers
&lt;/h2&gt;

&lt;p&gt;Security-Enhanced Linux (SELinux) is one of the most powerful built-in defenses on modern Linux systems. Unlike traditional permission-based security (user/group/other), SELinux adds &lt;strong&gt;mandatory access control (MAC)&lt;/strong&gt; at the kernel level. It labels every process, file, directory, and network port with a security context and enforces strict policies that say exactly what each subject is allowed to do with each object — no matter what the file permissions say.&lt;/p&gt;

&lt;p&gt;This means even if an attacker gains root or tricks a service into writing a malicious file, SELinux can still block the attack because the file simply doesn’t have the right context.&lt;br&gt;
Many enterprise Linux distributions enable SELinux &lt;strong&gt;by default&lt;/strong&gt; in enforcing mode on fresh installs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rocky Linux 9&lt;/li&gt;
&lt;li&gt;AlmaLinux 9&lt;/li&gt;
&lt;li&gt;Red Hat Enterprise Linux (RHEL) 9&lt;/li&gt;
&lt;li&gt;Fedora&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On these systems, SELinux is not an afterthought — it’s a core part of the security model.&lt;/p&gt;

&lt;h2&gt;
  
  
  How SELinux Stops Real-World Attacks
&lt;/h2&gt;

&lt;p&gt;Imagine an attacker sends a phishing email with a malicious script disguised as a legitimate configuration file. The user (or a compromised service) downloads and places the file in &lt;code&gt;/tmp&lt;/code&gt; or a web directory.&lt;/p&gt;

&lt;p&gt;Without SELinux:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the file has execute permissions, the attacker might be able to run it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With SELinux (enforcing mode):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The file gets created with the wrong security context (for example, &lt;code&gt;user_tmp_t&lt;/code&gt; instead of &lt;code&gt;httpd_exec_t&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Even if the attacker somehow makes the file executable, SELinux denies execution or access because the policy doesn’t allow it.&lt;/li&gt;
&lt;li&gt;The attack is stopped cold, and an audit log entry is generated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SELinux turns potential disasters into harmless denied operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Manual SELinux Management
&lt;/h2&gt;

&lt;p&gt;While SELinux is powerful, managing it consistently across many servers is painful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forgetting to run &lt;code&gt;restorecon&lt;/code&gt; after placing files&lt;/li&gt;
&lt;li&gt;Accidentally setting the wrong mode (setenforce)&lt;/li&gt;
&lt;li&gt;Configuration drift between hosts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s exactly why I built &lt;strong&gt;confdroid_selinux&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What confdroid_selinux Does
&lt;/h2&gt;

&lt;p&gt;This new Puppet 8 module (tested on Rocky 9) gives you full declarative control over SELinux:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installs all required SELinux tools and binaries&lt;/li&gt;
&lt;li&gt;Manages the main configuration file &lt;code&gt;/etc/sysconfig/selinux&lt;/code&gt; with correct permissions and SELinux contexts&lt;/li&gt;
&lt;li&gt;Controls the global SELinux mode (enforcing or permissive) — the Puppet equivalent of setenforce&lt;/li&gt;
&lt;li&gt;Ensures every file and directory managed by other Confdroid modules receives the proper SELinux context&lt;/li&gt;
&lt;li&gt;Works cleanly on enforcing-mode systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All other Confdroid modules (see the &lt;a href="https://sourcecode.confdroid.com/confdroid/puppet_collection" rel="noopener noreferrer"&gt;full collection overview&lt;/a&gt;) already include proper SELinux context handling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_apache" rel="noopener noreferrer"&gt;confdroid_apache&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_gitea" rel="noopener noreferrer"&gt;confdroid_gitea&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_php" rel="noopener noreferrer"&gt;confdroid_php&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_php" rel="noopener noreferrer"&gt;confdroid_fail2ban&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_automatic" rel="noopener noreferrer"&gt;confdroid_automatic&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_nrpe" rel="noopener noreferrer"&gt;confdroid_nrpe&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_nagios" rel="noopener noreferrer"&gt;confdroid_nagios&lt;/a&gt;,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and many more.&lt;/p&gt;

&lt;p&gt;They work even better when &lt;code&gt;**confdroid_selinux**&lt;/code&gt; is present, because the global policy and mode are managed in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  SELinux Management Flow with the Module
&lt;/h2&gt;

&lt;p&gt;Here’s how the module turns your Puppet run into reliable SELinux enforcement:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20z827cy5qyeoyp8iecz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20z827cy5qyeoyp8iecz.png" alt="Mermaid diagram" width="542" height="1166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy Deployment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple inclusion&lt;/strong&gt;:
in your site.pp or nodes.pp:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="nc"&gt;confdroid_selinux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;with Foreman (recommended)&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the &lt;code&gt;confdroid_selinux::params&lt;/code&gt; class to the host or host group and override parameters (mode, etc.) as smart class parameters.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important notes:&lt;br&gt;
Test in a non-production environment first.&lt;br&gt;
If you are switching from disabled to enforcing mode, a reboot is required (the module does not reboot automatically to avoid surprises).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can find the full module, source code, and parameter reference here:&lt;br&gt;
&lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_selinux" rel="noopener noreferrer"&gt;→&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;SELinux is no longer optional on modern enterprise Linux. With &lt;code&gt;confdroid_selinux&lt;/code&gt;, you get consistent, version-controlled, and fully automated SELinux management that works hand-in-hand with the rest of the Confdroid collection.&lt;br&gt;
Your servers stay secure by default — even when things go wrong elsewhere.&lt;br&gt;
Have you been running SELinux in enforcing mode across your fleet, or are you still in permissive because of management headaches? Would you like to see more advanced features (custom Booleans, custom modules, etc.) in a future version? Let me know in the comments!&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-pilot/" rel="noopener noreferrer"&gt;Confdroid Puppet Modules - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-puppet/" rel="noopener noreferrer"&gt;Confdroid Puppet Modules - Puppet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-resources/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - confdroid_resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-postgresql/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Postgresql&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-gitea/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Gitea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-apache/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-nagios/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Nagios&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-nrpe/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - NRPE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-fail2ban/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Fail2ban&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-automatic/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Automatic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-ssh/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - SSH&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-java/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>selinux</category>
      <category>security</category>
      <category>puppet</category>
      <category>foreman</category>
    </item>
    <item>
      <title>ConfDroid Puppet Modules - Automatic</title>
      <dc:creator>12ww1160</dc:creator>
      <pubDate>Tue, 07 Apr 2026 10:59:02 +0000</pubDate>
      <link>https://dev.to/12ww1160/confdroid-puppet-modules-automatic-537d</link>
      <guid>https://dev.to/12ww1160/confdroid-puppet-modules-automatic-537d</guid>
      <description>&lt;h2&gt;
  
  
  Introducing confdroid_automatic: Hands-Off OS Updates for Your Rocky 9 Servers
&lt;/h2&gt;

&lt;p&gt;Keeping operating systems patched is one of the most important — yet often neglected — parts of server maintenance. Security updates arrive regularly, and manually applying them across dozens or hundreds of machines quickly becomes a burden and a source of risk.&lt;/p&gt;

&lt;p&gt;That’s why I’m happy to release &lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_automatic" rel="noopener noreferrer"&gt;&lt;strong&gt;confdroid_automatic&lt;/strong&gt;&lt;/a&gt;, the latest addition to the Confdroid Puppet collection.&lt;/p&gt;

&lt;p&gt;This module brings reliable, automated OS updates to Rocky 9 (and other RHEL 9-based) systems by managing &lt;code&gt;dnf-automatic&lt;/code&gt; declaratively with Puppet 8. It installs the necessary packages, configures update behavior, applies correct SELinux contexts, and ensures the systemd timer runs as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Automated Updates Matter
&lt;/h2&gt;

&lt;p&gt;Security patches close vulnerabilities before attackers can exploit them. &lt;code&gt;dnf-automatic&lt;/code&gt; already does most of the heavy lifting out of the box — it can download and apply updates on a schedule, send notifications, and even reboot when needed. The challenge is managing it consistently across your entire fleet without configuration drift.&lt;/p&gt;

&lt;p&gt;Additionally, production systems may need a different set of policies for updates than development or staging system, i.e. only security updates, while other stages use fully updated systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;confdroid_automatic&lt;/strong&gt; solves that by turning dnf-automatic into a fully Puppet-controlled service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Installs and configures the &lt;code&gt;dnf-automatic&lt;/code&gt; package&lt;/li&gt;
&lt;li&gt;Manages the main configuration file (&lt;code&gt;/etc/dnf/automatic.conf&lt;/code&gt;) with proper permissions and SELinux contexts&lt;/li&gt;
&lt;li&gt;Controls the &lt;code&gt;dnf-automatic.timer&lt;/code&gt; systemd service&lt;/li&gt;
&lt;li&gt;Supports flexible parameters you can override via Foreman ENC or Hiera&lt;/li&gt;
&lt;li&gt;Includes sensible defaults for production use while allowing fine-tuning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Main tunable parameters include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ac_upgrade_type&lt;/code&gt;-  'default', 'security', 'minimal' or 'all'&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ac_apply_updates&lt;/code&gt; — whether to actually install updates (or just download them)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ac_download_updates&lt;/code&gt; — enable downloading of available packages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ac_random_sleep&lt;/code&gt; — add a random delay (in seconds) to prevent all servers from updating at the exact same moment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ac_reboot&lt;/code&gt; - when to reboot after applied updates&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ac_email_to&lt;/code&gt; - which email address to notify&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Automatic Update Flow
&lt;/h2&gt;

&lt;p&gt;Here’s how the module turns Puppet configuration into real-world automated patching:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxbnqljesfg108m7sdqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxbnqljesfg108m7sdqs.png" alt="Mermaid diagram" width="508" height="1780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flow ensures updates are applied safely and predictably. The optional random sleep helps avoid “thundering herd” problems in larger environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use It
&lt;/h2&gt;

&lt;p&gt;Import the module via r10k (Puppetfile).&lt;/p&gt;

&lt;p&gt;The simplest way to enable automatic updates on a node is in site.pp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="nc"&gt;confdroid_automatic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To apply via Foreman:&lt;/p&gt;

&lt;p&gt;Assign &lt;strong&gt;confdroid_automatic::params&lt;/strong&gt; to the host or hostgroup in Question and override parameters as required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test thoroughly in a non-production environment first. Automatic updates can cause reboots or service restarts.&lt;/li&gt;
&lt;li&gt;If you already have a manual dnf-automatic configuration, the module will overwrite it — start clean or review the generated config carefully.&lt;/li&gt;
&lt;li&gt;The module handles SELinux contexts automatically, so it works smoothly on enforcing-mode Rocky 9 systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the full module, source code, and parameter reference &lt;a href="https://sourcecode.confdroid.com/confdroid/confdroid_automatic" rel="noopener noreferrer"&gt;here: https://sourcecode.confdroid.com/confdroid/confdroid_automatic&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;With &lt;strong&gt;confdroid_automatic&lt;/strong&gt;, keeping your Rocky 9 fleet patched becomes a truly hands-off process. Combined with the rest of the Confdroid collection (including monitoring via &lt;code&gt;confdroid_nagios&lt;/code&gt;), you get a consistent, secure, and maintainable update strategy.&lt;/p&gt;

&lt;p&gt;Automated patching is no longer a nice-to-have — it’s a baseline security requirement. This module makes it simple, repeatable, and fully integrated into your Puppet workflow.&lt;/p&gt;

&lt;p&gt;Have you been managing OS updates manually or with scripts? Would you like automatic reboots enabled or prefer a download-only approach? Drop your thoughts or questions in the comments — I’d love to hear how you handle patching in your environment.&lt;/p&gt;




&lt;p&gt;Did you find this post helpful?  You can support me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/grizzly_coda" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2znn4km0i2jib7ru1sm9.png" alt="" width="170" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://confdroid.substack.com/subscribe?params=%5Bobject%20Object%5D" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r49xyl05rwkjb52xbqg.png" alt="Substack" width="250" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://feedback.confdroid.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7d47hck6vziak35nfgf.png" alt="ConfDroid Feedback Portal" width="300" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-pilot/" rel="noopener noreferrer"&gt;Confdroid Puppet Modules - Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-puppet/" rel="noopener noreferrer"&gt;Confdroid Puppet Modules - Puppet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-resources/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - confdroid_resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-postgresql/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Postgresql&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-gitea/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Gitea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-apache/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-nagios/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Nagios&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-nrpe/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - NRPE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-fail2ban/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Fail2ban&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-selinux/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - Selinux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-ssh/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - SSH&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confdroid.com/confdroid-puppet-module-java/" rel="noopener noreferrer"&gt;ConfDroid Puppet Modules - java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>confdroid</category>
      <category>updates</category>
      <category>puppet</category>
      <category>foreman</category>
    </item>
  </channel>
</rss>
