DevOps Blog

PostgreSQL & K8s: Run a Stateful Legacy App on a Stateless Microservice

Toye Idowu
3 minute read
Toye Idowu

In this blog post, we are going to expand on a previous article about statefulsets. I’ll show how to run and work with a database application, such as PostgreSQL, in Kubernetes.

To follow along I assume you have a Kubernetes cluster running and are familiar with k8s Service, Statefulset, Configmap, PersistentVolume, PersistentVolumeClaim and Docker images. (If you don’t, explore the K8s Guide, on the right). For us to deploy PostgresSQL on kubernetes, we need few things:

  • Postgres Docker Image to deploy.
  • Configmap for storing Postgres configurations.
  • Postgres Statefulset to deploy the pods and to auto create the PV/PVC.
  • Postgres Service to expose the statefulset.

(This article is part of our Kubernetes Guide. Use the right-hand menu to navigate.)


The first resource we need to create is the configurations we want to inject into postgres pod with a configmap. We want to pass in the username, password and the database.


apiVersion: v1
kind: ConfigMap
  name: postgres-config-demo
    app: postgres
  POSTGRES_DB: demopostgresdb
  POSTGRES_USER: demopostgresadmin
  POSTGRES_PASSWORD: demopostgrespwd

To create, simply run “kubectl create-f postgres-config.yaml”

Next resource to create is the postgres service so we can have multiple backends with a service that other services can connect with. Resource for deployment looks like:


apiVersion: v1
kind: Service
  name: postgres
    app: postgres
  - port: 5432
    name: postgres
  clusterIP: None
    app: postgres

Next resource to create is postgres statefulset. Resource for statefulset looks like:


apiVersion: apps/v1
kind: StatefulSet
  name: postgres-demo
  serviceName: "postgres"
  replicas: 2
      app: postgres
        app: postgres
      - name: postgres
        image: postgres:latest
          - configMapRef:
              name: postgres-config-demo
        - containerPort: 5432
          name: postgredb
        - name: postgredb
          mountPath: /var/lib/postgresql/data
          subPath: postgres
  - metadata:
      name: postgredb
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: gp2
          storage: 3Gi

In this yaml file, we can see that we are consuming the configmap we created earlier. We are also at the bottom of the file creating a volume claim automatically with the help of the storage class gp2. Refer to for more info.

The neat thing about statefulset is that it will create a volume for each of the pods. If any of the pods get deleted, the volume will persist. Let’s see what I mean:

Assuming the file above is created, if we describe any of the two pods, we will see in the event what is happening:

  Type    Reason                  Age   From                                                  Message
  ----    ------                  ----  ----                                                  -------
  Normal  Scheduled               1m    default-scheduler                                     Successfully assigned default/postgres-demo-0 to
  Normal  SuccessfulAttachVolume  1m    attachdetach-controller                               AttachVolume.Attach succeeded for volume "pvc-61f5163b-4aac-11e9-8d84-0692704f033a"
  Normal  Pulling                 1m    kubelet,  pulling image "postgres:latest"
  Normal  Pulled                  1m    kubelet,  Successfully pulled image "postgres:latest"
  Normal  Created                 1m    kubelet,  Created container
  Normal  Started                 1m    kubelet,  Started container

This line “AttachVolume.Attach succeeded for volume “pvc-61f5163b-4aac-11e9-8d84-0692704f033a”” tells us that a PVC is attached and a volume is created in aws. Something like the image below:

Lets verify that the configmap actually got injected by first checking the service that fronts out statefulset.

kubectl get service
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
postgres     NodePort           5432:31556/TCP   13s

Now we can connect with node port 31556 and the public IP address of the node our pod is running on. Keep in mind, i am using node port just to show how to quickly connect. It is not recommended to expose your database to public. If we run this command “psql -h <PUBLIC IP> -U demopostgresadmin –password -p 31556 demopostgresdb”, it will prompt for a password. We can enter the password we defined in configmap earlier. If all goes well we should see something like

psql (11.2, server 10.4 (Debian 10.4-2.pgdg90+1))
Type "help" for help.


To conclude, we used statefulset to deploy our postgres image along with PV/PVC, injecting configs into the pods using configmap, then exposing the postgres pods using a service that we can connected to on the NodePort.

Additional resources

For more on Kubernetes, explore these resources:

Beginning Kubernetes: Knowledge & Tutorials for Getting Started

In this comprehensive e-book, we take a deep dive into the distributed computing platform Kubernetes, also known as K8s.

These postings are my own and do not necessarily represent BMC's position, strategies, or opinion.

See an error or have a suggestion? Please let us know by emailing

BMC Bring the A-Game

From core to cloud to edge, BMC delivers the software and services that enable nearly 10,000 global customers, including 84% of the Forbes Global 100, to thrive in their ongoing evolution to an Autonomous Digital Enterprise.
Learn more about BMC ›

About the author

Toye Idowu

Toye Idowu

Olatoye is a Certified Kubernetes Administrator and experienced DevOps/Platform engineering Consultant with a demonstrated history of working in the Information Technology and Services industry.