Sign In
Sign In

Upgrading PostgreSQL Version

Upgrading PostgreSQL Version
Hostman Team
Technical writer
PostgreSQL
18.03.2024
Reading time: 7 min

In PostgreSQL, version upgrading is performed in different ways:

  • Installing packages with a new version of PostgreSQL. It is suitable only for minor updates. Before performing the update, study the release notes;
  • Using the standard pg_dumpall program. It is a reliable method, but there may be a long downtime.

  • Using the standard pg_upgrade program. Another quick way to upgrade, but errors may occur.

  • Updating via logical replication. This option has minimal downtime but is only suitable for PostgreSQL versions greater than 10.0. Earlier versions require extensions.

The choice depends on which release is used on the server and which version you want to upgrade to.

In this article, we will look at all the above upgrade methods.

Important notes before upgrading

The main thing is to understand the peculiarities of updates between different versions.

The numbers consist of two digits, for example, 10.1. The first digit is the major version number (10). The second digit is the minor release number (1).

Before PostgreSQL 10, the version numbers consisted of three digits. For example, in 9.6.3, 9.6 is the major release number and 3 is the minor version number.

You must understand this difference to choose the right upgrade method.

In minor versions, the data storage format does not change, so there are no compatibility problems. Therefore, the transition from PostgreSQL 10.1 to 10.6 can be carried very easily. To upgrade, turn off the server, replace the executable files, and start the server again.

However, the documentation notes that some releases may require manual changes. Therefore, always read the release notes before upgrading.

In major versions, the data format may change. This makes updating more difficult. You need to either unload the data and upload it again, use the pg_upgrade program, or use logical replication.

We'll talk about all these methods below.

Upgrading within one major version

Let's look at how to update Postgresql Version 14.1 to PostgreSQL 14.3 on an Ubuntu server.

PostgreSQL is available on Ubuntu by default. Start with the command:

sudo apt-get upgrade

Debian and Ubuntu only release one version of PostgreSQL per OS release. For example, Debian Squeeze/6.0 only has PostgreSQL 8.4. If you need a different version of PostgreSQL, use packages from PGDG.

If you want the latest version of PostgreSQL, you must first install the Postgres repository.

Add a repository:

sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'

Import the signing key:

wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -

Update the list of packages in the system:

sudo apt-get update

Install the required Postgres version:

sudo apt-get -y install postgresql-14.3

To view the list of installed DBMSs, run:

dpkg --get-selections | grep postgres

Also see the list of clusters:

pg_lsclusters

Stop PostgreSQL before making changes:

sudo service postgresql stop

When Postgres packages are installed, they create a default cluster for you. You should rename the new Postgres cluster so there are no conflicts with the old cluster name when upgrading.

sudo pg_renamecluster 14.3 main main_pristine

Upgrade the old cluster:

sudo pg_upgradecluster 14.1 main

Start the service:

sudo service postgresql start

Check the list of clusters again and make sure the new one is working:

pg_lsclusters

Get rid of the old cluster:

sudo pg_dropcluster 14.1 main

Upgrading via pg_dumpall

If you need to change the major version, use the pg_dumpall program. The essence of this method is to unload data from one main version and then load it into another.

  1. Before unloading data, make sure that no changes are being made to the database right now. Otherwise, some of the changes may not be included in the final dump.
  2. Get the dump and write it to a file:
pg_dumpall > output_file
  1. Stop the server:
pg_ctl stop
  1. Change the name of the old directory to eliminate name conflicts:
mv /usr/local/pgsql /usr/local/pgsql.old

The old directory can simply be deleted. But it would be wiser to rename it to leave room for restoring data. Instead of /usr/local/pgsql, specify the path to your directory.

  1. Install the new version from source. There is a detailed guide in the documentation. 
  2. Form a new cluster:
/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data
  1. Transfer all changes to the pg_hba.conf and postgresql.conf files.
  2. Start the database server:
/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data
  1. Restore data from backup:
/usr/local/pgsql/bin/psql -d postgres -f output_file

The disadvantage of this method is that the server will be turned off for a long time. To reduce downtime, you can install the new server in a different directory and then run the old and new servers on different ports. To transfer data, use the command:

pg_dumpall -p 5432 | psql -d postgres -p 5433

Instead of 5432 and 5433, specify the port numbers on which you ran the servers.

Upgrading via pg_upgrade

To avoid unloading and uploading data, use the pg_upgrade program. It helps you update faster. The pg_upgrade program creates system tables anew, taking into account changes in the PostgreSQL latest versions. In this case, the old data files are retained. In addition, pg_upgrade verifies that the clusters are compatible.

The pg_upgrade program helps you upgrade from PostgreSQL 8.4.X to the current DBMS release.

Let's look at a general upgrade plan using pg_upgrade.

  1. Move the old cluster. This must be done if the directory was not linked to the old release. For example, it is located at /usr/local/pgsql. If you do not rename it, a conflict will occur during the update.

Renaming is performed when the server is turned off with the command:

mv /usr/local/pgsql /usr/local/pgsql.old
  1. Build a new version from source. Adjust configure so that the flags are compatible with the old cluster configuration. Before starting the upgrade, pg_upgrade will check the compatibility of the flags.
  2. Install new executable files. To place the server in a non-standard directory, use prefix:
make prefix=/usr/local/pgsql.new install
  1. Use initdb to initialize a new cluster. Check that the flags are compatible with the flags of the old cluster.
  2. Install extension shared object files. If updates are available, pg_upgrade will automatically create a script for their subsequent installation.
  3. Transfer full text search files.
  4. Configure peer authentication in pg_hba.conf. This needs to be done because pg_upgrade will connect to the old and new hosts multiple times.
  5. Check that the old and new servers are stopped:
pg_ctl -D /opt/PostgreSQL/9.6 stop
pg_ctl -D /opt/PostgreSQL/14 stop
  1. Run pg_upgrade from the new server. First, call the program in check mode by adding the flag:
pg_upgrade --check

In response, you will receive a list of adjustments that need to be made manually after the upgrade.

  1. If you made changes to the pg_hba.conf file, restore it to its previou state. Sometimes, other configurations on the new cluster need to be changed to match the settings of the old cluster.
  2. Start the server.

If you are satisfied with the result, delete the old cluster.

For details and features of using pg_upgrade, see the documentation.

Using Logical Replication

Starting from the 10th version of PostgreSQL, we have a method for logical replication of data objects and changes in them. It is based on the use of replication identifiers; usually, they are primary keys.

Logical replication uses the publish-subscribe model. The user creates a snapshot of the published database and copies it to the subscriber. In the PostgreSQL documentation, one of the typical scenarios for using logical replication is precisely the replication between different major versions of PostgreSQL.

The backup server can be located on the same or different host. After synchronization is complete, various options are available. For example, you can make the new server the main one and turn off the old one.

The main advantage of logical replication is minimal downtime. Server interruption, in some cases, is limited to a few seconds.

Conclusion

We looked at universal methods for upgrading PostgreSQL to a new major version and updating within one major version.

Using this guide, you can select the method that works best for your particular case and update your current version of PostgreSQL.

Hostman provides pre-configured and ready-to-use cloud databases, including cloud PostgreSQL.

PostgreSQL
18.03.2024
Reading time: 7 min

Similar

PostgreSQL

PostgreSQL Triggers: Creation, Deletion, Examples

Triggers are used to instruct the PostgreSQL engine to execute a piece of code when a specific event occurs. They act like a catalyst for changes — a trigger that initiates a chain of events. A trigger must be associated with a specific table, view (pseudo-table), or foreign table. It activates only when operations are performed on that entity — INSERT, UPDATE, DELETE, or TRUNCATE. Depending on your needs, the trigger can run before, after, or instead of the event/operation. Types of Triggers PotgreSQL triggers are divided into two types depending on the level at which they operate: FOR EACH ROW: The function is called for each individual row affected by the event. For example, an UPDATE affecting 100 rows will trigger the function 100 times — once for each row. FOR EACH STATEMENT: The function is called just once per SQL statement, regardless of how many rows are affected. Triggers Use Cases Triggers are powerful tools with many use cases. Some examples include: Tracking Changes: You can use triggers to log transaction details when changes occur in a table. Data Validation: Triggers can enforce constraints before applying changes to the database. Auto-Filling Fields: Automatically populate fields based on new transaction data. Performance Optimization: For example, on a server like Hostman, you might log timestamps in a table and want to aggregate that data every 6 hours (four intervals a day). Scanning and aggregating all rows repeatedly is inefficient on large datasets. Instead of recalculating everything each time, you can use Materialized Views, which cache results. However, these are recalculated entirely on each refresh, which is still problematic on large datasets. Triggers can solve this by acting like "smart" materialized views: updating only the affected row, not the entire dataset. Creating a Trigger Let’s look at how to create a trigger in PostgreSQL. Syntax: CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] } ON table_name [ FROM referenced_table_name ] [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ] [ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ] [ FOR [ EACH ] { ROW | STATEMENT } ] [ WHEN ( condition ) ] EXECUTE { FUNCTION | PROCEDURE } function_name ( arguments ); Where the event can be one or more of the following: INSERT UPDATE [ OF column_name [, ...] ] DELETE TRUNCATE Key notes: You can create (CREATE) or replace (REPLACE) an existing trigger. You associate the function directly with a table/view/foreign table. The code runs only when the specified event occurs on that entity. Triggers using INSTEAD OF must be marked with FOR EACH ROW and are only allowed on views. Triggers using BEFORE or AFTER on views must be FOR EACH STATEMENT. Refer to PostgreSQL documentation for a summary table that outlines these rules. Basic Examples of PostgreSQL Triggers Trigger before update: CREATE TRIGGER check_update BEFORE UPDATE ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_update(); Trigger before update of a specific column: CREATE OR REPLACE TRIGGER check_update BEFORE UPDATE OF balance ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_update(); Trigger after update to log changes: CREATE TRIGGER log_update AFTER UPDATE ON accounts FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE FUNCTION log_account_update(); INSTEAD OF trigger for view insertions: CREATE TRIGGER view_insert INSTEAD OF INSERT ON my_view FOR EACH ROW EXECUTE FUNCTION view_insert_row(); Trigger on delete: CREATE TRIGGER example_delete_trigger AFTER DELETE ON my_view FOR EACH ROW EXECUTE PROCEDURE aft_delete(); Practical Example: Inserting Into Two Tables Let’s create a PostgreSQL trigger that adds information about a new employee to a second table when data is inserted into the primary table. Step 1: Create both tables: CREATE TABLE "Employee" ( "EmployeeId" INT NOT NULL, "LastName" VARCHAR(20) NOT NULL, "FirstName" VARCHAR(20) NOT NULL, "Title" VARCHAR(30), "ReportsTo" INT, "BirthDate" TIMESTAMP, "HireDate" TIMESTAMP, "Address" VARCHAR(70), "City" VARCHAR(40), "State" VARCHAR(40), "Country" VARCHAR(40), "PostalCode" VARCHAR(10), "Phone" VARCHAR(24), "Fax" VARCHAR(24), "Email" VARCHAR(60), CONSTRAINT "PK_Employee" PRIMARY KEY ("EmployeeId") ); CREATE TABLE "Employee_Audit" ( "EmployeeId" INT NOT NULL, "LastName" VARCHAR(20) NOT NULL, "FirstName" VARCHAR(20) NOT NULL, "UserName" VARCHAR(20) NOT NULL, "EmpAdditionTime" VARCHAR(20) NOT NULL ); Step 2: Create the trigger function: CREATE OR REPLACE FUNCTION employee_insert_trigger_fnc() RETURNS trigger AS $$ BEGIN INSERT INTO "Employee_Audit" ("EmployeeId", "LastName", "FirstName", "UserName", "EmpAdditionTime") VALUES (NEW."EmployeeId", NEW."LastName", NEW."FirstName", current_user, current_date); RETURN NEW; END; $$ LANGUAGE 'plpgsql'; Step 3: Create the trigger: CREATE TRIGGER employee_insert_trigger AFTER INSERT ON "Employee" FOR EACH ROW EXECUTE PROCEDURE employee_insert_trigger_fnc(); Test: Insert a record: INSERT INTO "Employee" VALUES(12, 'Smith', 'Jeff', 'Editor', 1, '1992-05-28 00:00:00', '2022-01-15 00:00:00', 'Paseo de Gracia', 'Barcelona', 'Catalonia', 'Spain', '128 665', '+15 52-469-2573', '+15 52-469-2573', 'mail@mail.com'); Check that the data is there: SELECT * FROM "Employee" WHERE "EmployeeId" = 12; Check the audit log: SELECT * FROM "Employee_Audit"; You should see something like: EmployeeId | 12 LastName | Smith FirstName | Jeff UserName | postgres EmpAdditionTime | 2025-04-10 Perfect! It works as expected. Modifying a Trigger To change a trigger's properties, use CREATE OR REPLACE TRIGGER and specify the same trigger function and table. You can update the remaining properties as needed. To rename a trigger: ALTER TRIGGER name ON table_name RENAME TO new_name; Check documentation for more details. Deleting a Trigger To delete a trigger: DROP TRIGGER [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ]; Example: DROP TRIGGER some_example_of_trigger ON "Example"; Note: You must own the table to delete its trigger. Use IF EXISTS to avoid errors if the trigger doesn’t exist. Use CASCADE to remove all dependent objects. Use RESTRICT to prevent deletion if dependencies exist (default). Check the documentation for more details. Important Notes You must have TRIGGER privilege on the table and EXECUTE privilege on the trigger function. You can check the system catalog pg_trigger to view existing triggers. If you define multiple triggers for the same table/event, they execute alphabetically by name.
11 April 2025 · 6 min to read
PostgreSQL

How to Install PostgreSQL on Ubuntu

PostgreSQL is a well-known relational database management system that provides high-availability features. These are renowned for their functionalities, such as support for complex statements, views, triggers, and foreign keys. PostgreSQL is installed on Ubuntu to provide a secure as well as flexible DB infrastructure. By installing it on the Linux distribution, you can enable and deal with the vast data in an efficient and secure manner. This infrastructure is designed to deal with different workloads, from single-machine apps to large internet-facing apps with multiple concurrent users. In this tutorial, we will walk through installing and setting up PostgreSQL on Ubuntu. Installing PostgreSQL on Ubuntu Ubuntu comes with the PostgreSQL package in its default repositories. You can install it on a Linux system following the given steps. It downloads the stable package. This is important for DB analysts, administrators, and industries that require a dependable DB solution for managing a large volume of data.  Let’s move forward into each step of installing PostgreSQL with detailed explanations on Ubuntu. Step 1: Refresh Ubuntu Repository First, refresh the Ubuntu package indexes. It is useful for maintaining system stability and security: sudo apt update Step 2: PostgreSQL Installation  PostgreSQL is included in Ubuntu's default repositories, making installation straightforward. To install it with some additional tools, execute the below command: sudo apt install -y postgresql postgresql-contrib Here: postgresql: The core PostgreSQL DB server. postgresql-contrib: Additional utilities and extensions useful for DB. Step 3: Start and Enable Services It is essential to enable the PostgreSQL service after installation and ensure it starts automatically upon bootup. The installed package utilizes the systemd daemon, which deals with the DB server runtime operations.  Run the commands below to make sure the DB server automatically initializes when the server boots up. Let’s initialize the service: sudo systemctl start postgresql And enable it so that it launches automatically when the machine boots up: sudo systemctl enable postgresql Step 4: Verify Installation To verify if the installed package is operating properly, check out its status through the below: sudo systemctl status postgresql In the figure, you can see an active status. It represents the services that are correctly running. Step 5: Access Shell Finally, switch to the PostgreSQL user account and launch the CLI to interact with database operations. Here, the -i flag provides an interactive login shell: sudo -i -u postgres The above display gives access to a user account. Once you're in the user environment, launch the CLI through the given command: psql As you can see in the above screenshot, this command launchs the CLI, where users can perform multiple operations by executing commands. Step 6: Basic Database Commands The creation of a database and a new user for any application is a good practice rather than using the root. In this way, a particular user can access the shell interface and run basic statements on the particular database. Here are general commands with thorough explanations and examples: Creation of the New Database To prevent conflicts, ensure data integrity and control access privileges for different applications or users, you need to create a new database. For creation, use the given command within the shell: CREATE DATABASE hostmandb; The screenshot shows that a database has been created. Creation of the New User Create a customized user and set a strong password: CREATE USER hostman_user WITH ENCRYPTED PASSWORD 'Qwer1234'; We have created a hostman_user with the password Qwer1234. Give Privileges to the Created User You can grant hostman_user complete privileges to the hostmandb through the given command. This allows controlled accessibility to database objects, enhancing data integrity as well as security: GRANT ALL PRIVILEGES ON DATABASE hostmandb TO hostman_user; Exit Shell For closing the shell, you can utilize the \q option as below: \q The figure shows that the shell has been exited. Finally, you can exit the user environment by executing the given command: exit The output confirms that you have returned to the main system user account. Access a Database as the Particular User You can connect to the particular database from the specific user account through the -U flag: psql -U hostman_user -d hostmandb -h 127.0.0.1 -p 5432 Here: -U hostman_user: Indicates the user. -d hostmandb: Indicates the database name. -h 127.0.0.1: Indicates the host (local machine). -p 5432: Indicates the PostgreSQL default port. In the given output, you can see that the user hostman_user has accessed the hostmandb console. Listing of All Databases View all databases and check whether the hostmandb database is available or not: \l In the figure, you can see that the database hostmandb is listed. Switch/Connect to the Particular Database To switch to a specific database, such as hostmandb, within PostgreSQL, use the \c command: \c hostmandb The above output confirms that the postgres user has successfully connected to the hostmandb database. Creation of the Particular Table To create a customized table, use the below statement. In our example, we create a hostmantb table: CREATE TABLE hostmantb(    id SERIAL PRIMARY KEY,    name VARCHAR(100),    age INTEGER,    department_id INTEGER); Here: id SERIAL PRIMARY KEY: It creates an auto-incrementing primary key column with the name id. name VARCHAR(100): It creates a name column that can store up to 100 characters. age INTEGER: It creates an age column that stores integer values. department_id INTEGER: This line creates a department_id column that stores integer values. The outcome of the above screenshot confirms that the table hostmantb is successfully created with specific attributes. List All Tables in the Particular Database For listing all tables in the hostmandb database, use the \dt command: \dt It displays all tables as well as confirms that the hostmantb is listed above. Overview a Table's Structure You can view the structure of the created table through the \d command. Let’s display the structure of hostmantb table: \d hostmantb In the above figure, you can see the complete structure of hostmantb table with id, name, age, and department_id having specific types. Input Entries into a Particular Table To input entries into a hostmantb table, use the below statement. This way, we input the values Peter, 35, and 1 to the columns name, age, and department_id, respectively: INSERT INTO hostmantb(name, age, department_id) VALUES ('Peter', 35, 1); The output confirms that we have successfully input data into the hostmantb table. Select All Data from a Particular Table You can select all data from the hostmantb table through the below statement: SELECT * FROM hostmantb; Modify Data in Particular Table For modifying data in hostmantb, you can utilize the below statement. For example, set the age column to 40 for the row where the name column equals Peter: UPDATE hostmantb SET age = 40 WHERE name = 'Peter'; The output confirms that the age column is updated. Remove Data from the Particular Table In this section, you can remove data from the specific table through the DELETE statement. It deletes all entries from the hostmantb table where the value in the name column is Peter: DELETE FROM hostmantb WHERE name = 'Peter'; In the screenshot, one entry from the hostmantb table is removed. Exit CLI You can easily exit the CLI via the \q utility: \q This tutorial has given you thorough guidelines for every step involved in installing as well as setting PostgreSQL on Ubuntu.  Conclusion By installing PostgreSQL on Ubuntu, you can optimize the DB infrastructure. PostgreSQL is often chosen for its freely available nature, which allows for customization, the system's stability and security features. In addition, PostgreSQL's supporters' help and comprehensive guide make it simpler to enhance operations and troubleshoot problems.  This combination makes sure that the DB system is both powerful as well as adaptable to several needs. In this tutorial, there are a lot of fundamental DB commands to get you started with database administration. Therefore, you will be able to create databases, and users and perform basic tasks.
24 January 2025 · 7 min to read
Kubernetes

How to Deploy PostgreSQL on Kubernetes

PostgreSQL is a popular relational database management system (RDBMS) that provides high-availability features like streaming replication, logical replication, and failover solutions. Deploying PostgreSQL on Kubernetes allows organizations to build resilient systems that ensure minimal downtime and data availability. With Kubernetes StatefulSets, you can scale PostgreSQL deployment in response to demand. Kubernetes Environment Setup To get started, make sure you have the following: Kubernetes Cluster (Cloud or Local):  You can set up a Kubernetes cluster on Hostman within no time. To follow this tutorial with a local Kubernetes cluster, you can use one of these tools: k3s, minikube, microk8s, kind. Kubectl: Kubectl allows users to interact with a Kubernetes cluster. The kubectl needs a configuration YAML file which contains cluster details and is usually provided by your cloud provider.  From the Hostman control panel, you can simply download this configuration file with a click of a button as indicated in the below screenshot. To connect, you need to set KUBECONFIG environment variable accordingly. export KUBECONFIG=/absolute/path/to/file/k8s-cluster-config.yaml Helm: You need Helm CLI to install Helm charts. Helm version 3 is required. Deploy PostgreSQL Using a Helm Chart Helm is a package manager for Kubernetes just like apt for Ubuntu and Debian. Instead of manually creating multiple YAML files for Pods, Services, Persistent Volumes, Secrets, etc., the Helm chart simplifies this to a single command (e.g., helm install), streamlining the deployment process. Step 1: Add helm repository To add the Bitnami PostgreSQL Helm repo, run this command: helm repo add bitnami https://charts.bitnami.com/bitnami To sync your local Helm repository with the remote one: helm repo update Step 2: Manage Data Persistence PostgreSQL requires persistent storage to ensure that data is preserved even if a pod crashes or is rescheduled. When a Persistent Volume Claim (PVC) is combined with a Persistent Volume (PV), Kubernetes can allocate a desired chunk of storage either in disk or cloud storage. PVC requests the Kubernetes cluster for storage space. Kubernetes then looks at the available PVs and assigns one to it. Create a file named postgres-local-pv.yaml with the YAML manifest: apiVersion: v1 kind: PersistentVolume metadata: name: postgresql-local-pv spec: capacity: storage: 5Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: manual hostPath: path: /mnt/data/postgresql This manifest creates a PersistentVolume backed by a local directory (/mnt/data/postgresql) on a specific node. This means if the node goes down or becomes unavailable, the data stored in that PV will be inaccessible, which is a critical risk in production. Therefore, it’s highly recommended to use cloud-native storage solutions instead of hostPath to ensure reliability, scalability and data protection. This PV has a reclaim policy of Retain, ensuring that it is not deleted when no longer in use by a PVC. You can set storageClassName to ceph-storage, glusterfs, portworx-sc, or openebs-standard based on your needs. Create a file named postgres-local-pvc.yaml with this text: apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgresql-local-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi storageClassName: manual The ReadWriteOnce config means the volume can be read-write by a single node at a time. You might think, replacing it with ReadWriteMany will make your application highly available. This isn’t the case. ReadWriteMany (RWX) access mode allows multiple pods to access the same PersistentVolume simultaneously, this can indeed create serious issues leading to potential race conditions, data corruption, or inconsistent state. Apply these manifests using kubectl and create new resources. kubectl apply -f postgres-local-pv.yamlkubectl apply -f postgres-local-pvc.yaml Step 3: Install PostgreSQL Helm Chart Run the following command to install the Helm chart. helm install tutorial-db bitnami/postgresql --set auth.username=bhuwan \ --set auth.password=”AeSeigh2gieshe” \ --set auth.database=k8s-tutorial \ --set auth.postgresPassword=”Ze4hahshez6dop9vaing” \ --set primary.persistence.existingClaim=postgresql-local-pvc \ --set volumePermissions.enabled=true After a couple of minutes, verify if things have worked successfully with this command: kubectl get all Step 4: Test and Connect The following command runs a temporary PostgreSQL client pod. The pod connects to the database named k8s-tutorial, using the username bhuwan and the password from the environment variable $POSTGRES_PASSWORD. export POSTGRES_PASSWORD=$(kubectl get secret --namespace default tutorial-db-postgresql -o jsonpath="{.data.password}" | base64 -d) kubectl run tutorial-db-postgresql-client --rm --tty -i --restart='Never' \ --image docker.io/bitnami/postgresql:17.2.0-debian-12-r6 \ --env="PGPASSWORD=$POSTGRES_PASSWORD" \ --command -- psql --host tutorial-db-postgresql \ -U bhuwan -d k8s-tutorial -p 5432 After the session ends, the pod will be deleted automatically due to the --rm flag. A quick reminder, if you have changed the Helm chart release name, users, or database name, adjust the above commands accordingly. Deploy Postgres on Kubernetes from scratch A StatefulSet is the best Kubernetes resource for deploying stateful applications like PostgreSQL. This way, every PostgreSQL pod gets its own stable network identities and persistent volumes. Note: you’ll be using a previously created Persistent Volume Claim (PVC) and Persistent Volume(PV). So, do some cleanup and recreate those resources. helm delete tutorial-db kubectl delete pvc postgresql-local-pvc kubectl delete pv postgresql-local-pv kubectl apply -f postgres-local-pv.yaml -f postgres-local-pvc.yaml Create a file named postgres-statefulset.yaml with the following text: apiVersion: apps/v1 kind: StatefulSet metadata: name: postgres-statefulset labels: app: postgres spec: serviceName: "postgresql-headless-svc" replicas: 1 selector: matchLabels: app: postgres template: metadata: labels: app: postgres spec: containers: - name: postgres image: postgres:17.2 envFrom: - secretRef: name: postgresql-secret ports: - containerPort: 5432 name: postgresdb volumeMounts: - name: pv-data mountPath: /var/lib/postgresql/db volumes: - name: pv-data persistentVolumeClaim: claimName: postgresql-local-pvc Before you can apply these changes, create a new Secret for handling sensitive details like passwords with kubectl. kubectl create secret generic postgresql-secret --from-literal=POSTGRES_PASSWORD=Ze4hahshez6dop9vaing kubectl apply -f postgres-statefulset.yaml If the pod gets stuck with Pending state, you can try creating a StorageClass with the following manifest. kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: manual provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer To investigate any further issues with the pod, you can use the command: kubectl describe pod postgres-statefulset-0 This command will report any issues related to scheduling the pod to a node, mounting volumes, or resource constraints. Databases like PostgreSQL are typically accessed internally by other services or applications within the cluster, so it's better to create a Headless service for it. Create a file called postgres-service.yaml and include the following YAML manifest: apiVersion: v1 kind: Service metadata: name: postgresql-headless-svc spec: type: ClusterIP selector: app: postgres ports: - port: 5432 targetPort: 5432 clusterIP: None Finally, you can test the connection with kubectl run. kubectl run tutorial-db-postgresql-client --rm --tty -i --restart='Never' \ --image docker.io/bitnami/postgresql:17.2.0-debian-12-r6 \ --env="PGPASSWORD=Ze4hahshez6dop9vaing" \ --command -- psql --host postgres-statefulset-0.postgresql-headless-svc \ -U postgres -p 5432 Scale, Replication, and Backup To scale up a Statefulset, simply pass the number of replicas with --replicas flag.  kubectl scale statefulset postgres-statefulset --replicas=3  To reach replicas, you can make use of headless service. For instance, with hostname postgres-statefulset-1.postgresql-headless-svc you can send requests to pod 1. For handling backups, you can use CronJob with the pg_dump utility provided by PostgreSQL. Best Practices Throughout the tutorial, the decision to handle passwords via Kubernetes Secret, using StatefulSet instead of Deployment was a good move. To make this deployment even more secure, reliable, and highly available, here are some ideas: Set Resource Requests and Limits: Set appropriate CPU and memory requests and limits to avoid over-provisioning and under-provisioning. Backups: Use Kubernetes CronJobs to regularly back up your PostgreSQL data. Consider implementing Volume Snapshots as well. Monitoring and Log Postgresql: You can use tools like Prometheus and Grafana to collect and visualize PostgreSQL metrics, such as query performance, disk usage, and replication status. Use Pod Disruption Budgets (PDBs): If too many PostgreSQL pods are disrupted at once (e.g., during a rolling update), it can lead to database unavailability or replication issues. Conclusion Helm chart is the recommended way of complex and production deployment. Helm provides an automated version manager alongside hiding the complexities of configuring individual Kubernetes components. Using the Helm template command, you can even render the Helm chart locally and make necessary adjustments with its YAML Kubernetes manifests. Kubernetes provides scalability, flexibility, and ease of automation for PostgreSQL databases. By leveraging Kubernetes features like StatefulSets, PVCs, PDBs, and secrets management, you can ensure that your PostgreSQL database is tuned for the production environment.
24 January 2025 · 8 min to read

Do you have questions,
comments, or concerns?

Our professionals are available to assist you at any moment,
whether you need help or are just unsure of where to start.
Email us
Hostman's Support