FerretDB with PostgreSQL Cluster (Repmgr)

In this example, we'll deploy the Ferretdb with its PostgreSQL (replication and failover) database.
FerretDB allows you to use MongoDB drivers seamlessly with PostgreSQL as the database backend. Use all tools, drivers, UIs, and the same query language and stay open-source.

1. Create shared variables

Go to "Environment variables" menu and create variables.

Don't forget to check the "hide value" box.
  • SHARED_POSTGRESQL_POSTGRES_PASSWORD
  • SHARED_POSTGRESQL_PASSWORD (password for POSTGRESQL_USERNAME defined in environmentVariables for pgsql-* services -> appuser in this example)
  • SHARED_REPMGR_PASSWORD
  • SHARED_LB_STATS_PASSWORD (password for admin user - Haproxy stats web interface)
  • SHARED_ME_CONFIG_BASICAUTH_PASSWORD (password for admin user - htaccess)

create postgres env vars

2. Import feature to create services

Here we'll look at how to déploy 7 services with a single YAML file:

  • 3 PostGreSQL nodes
  • 2 Load Balancers for PostGresSQL
  • 1 FerretDB
  • 1 MongoExpress

Import YAML definition :

Classic environment
services:
  - id: pgsql0
    name: pgsql-0
    dockerConfiguration:
      image: bitnami/postgresql-repmgr
      imageVersion: "latest"
      hostname: "pgsql-0" # must ended by -"Number"
    countMin: 1 # 1 service deployed
    countMax: 1 
    cpuLimit: 512 # MHz
    memoryLimitMiB: 512
    ports:
      - listeningPort: 54320
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
      - SHARED_POSTGRESQL_PASSWORD
      - SHARED_REPMGR_PASSWORD
    environmentVariables:
      - key: REPMGR_NODE_NAME
        value: "pgsql-0" # same as hostname
      - key: POSTGRESQL_PORT_NUMBER
        value: "54320"
      - key: REPMGR_PRIMARY_HOST
        value: "127.0.0.1"
      - key: REPMGR_PRIMARY_PORT
        value: "54320"
      - key: REPMGR_PORT_NUMBER
        value: "54320"
      - key: REPMGR_PARTNER_NODES
        value: "127.0.0.1:54320,%PG1_ADD%,%PG2_ADD%"
      - key: REPMGR_NODE_NETWORK_NAME
        value: "127.0.0.1"
      - key: POSTGRESQL_POSTGRES_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: POSTGRESQL_USERNAME # user to create
        value: "appuser"
      - key: POSTGRESQL_PASSWORD
        value: "%SHARED_POSTGRESQL_PASSWORD%"
      - key: POSTGRESQL_DATABASE # database to create
        value: "appdb"
      - key: REPMGR_USERNAME
        value: "repmgr"
      - key: REPMGR_PASSWORD
        value: "%SHARED_REPMGR_PASSWORD%"
    links:
      - toServiceId: pgsql1
        toServicePort: 54321
        localExposedPort: 54321
        variableHost: PG1_HOST
        variablePort: PG1_PORT
        variableAddress: PG1_ADD
      - toServiceId: pgsql2
        toServicePort: 54322
        localExposedPort: 54322
        variableHost: PG2_HOST
        variablePort: PG2_PORT
        variableAddress: PG2_ADD
  - id: pgsql1
    name: pgsql-1
    dockerConfiguration:
      image: bitnami/postgresql-repmgr
      imageVersion: "latest"
      hostname: "pgsql-1" # must ended by -"Number"
    countMin: 1 # 1 service deployed
    countMax: 1
    cpuLimit: 512 # MHz
    memoryLimitMiB: 512
    ports:
      - listeningPort: 54321
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
      - SHARED_POSTGRESQL_PASSWORD
      - SHARED_REPMGR_PASSWORD
    environmentVariables:
      - key: REPMGR_NODE_NAME
        value: "pgsql-1"  # same as hostname
      - key: POSTGRESQL_PORT_NUMBER
        value: "54321"
      - key: REPMGR_PRIMARY_HOST
        value: "%PG0_HOST%"
      - key: REPMGR_PRIMARY_PORT
        value: "%PG0_PORT%"
      - key: REPMGR_PORT_NUMBER
        value: "54321"
      - key: REPMGR_PARTNER_NODES
        value: "%PG0_ADD%,127.0.0.1:54321,%PG2_ADD%"
      - key: REPMGR_NODE_NETWORK_NAME
        value: "127.0.0.1"
      - key: POSTGRESQL_POSTGRES_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: POSTGRESQL_USERNAME
        value: "appuser"
      - key: POSTGRESQL_PASSWORD
        value: "%SHARED_POSTGRESQL_PASSWORD%"
      - key: POSTGRESQL_DATABASE
        value: "appdb"
      - key: REPMGR_USERNAME
        value: "repmgr"
      - key: REPMGR_PASSWORD
        value: "%SHARED_REPMGR_PASSWORD%"
    links:
      - toServiceId: pgsql0
        toServicePort: 54320
        localExposedPort: 54320
        variableHost: PG0_HOST
        variablePort: PG0_PORT
        variableAddress: PG0_ADD
      - toServiceId: pgsql2
        toServicePort: 54322
        localExposedPort: 54322
        variableHost: PG2_HOST
        variablePort: PG2_PORT
        variableAddress: PG2_ADD
  - id: pgsql2
    name: pgsql-2
    dockerConfiguration:
      image: bitnami/postgresql-repmgr
      imageVersion: "latest"
      hostname: "pgsql-2" # must ended by -"Number"
    countMin: 1 # 1 service deployed
    countMax: 1
    cpuLimit: 512 # MHz
    memoryLimitMiB: 512
    ports:
      - listeningPort: 54322
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
      - SHARED_POSTGRESQL_PASSWORD
      - SHARED_REPMGR_PASSWORD
    environmentVariables:
      - key: REPMGR_NODE_NAME
        value: "pgsql-2"  # same as hostname
      - key: POSTGRESQL_PORT_NUMBER
        value: "54322"
      - key: REPMGR_PRIMARY_HOST
        value: "%PG0_HOST%"
      - key: REPMGR_PRIMARY_PORT
        value: "%PG0_PORT%"
      - key: REPMGR_PORT_NUMBER
        value: "54322"
      - key: REPMGR_PARTNER_NODES
        value: "%PG0_ADD%,%PG1_ADD%,127.0.0.1:54322"
      - key: REPMGR_NODE_NETWORK_NAME
        value: "127.0.0.1"
      - key: POSTGRESQL_POSTGRES_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: POSTGRESQL_USERNAME
        value: "appuser"
      - key: POSTGRESQL_PASSWORD
        value: "%SHARED_POSTGRESQL_PASSWORD%"
      - key: POSTGRESQL_DATABASE
        value: "appdb"
      - key: REPMGR_USERNAME
        value: "repmgr"
      - key: REPMGR_PASSWORD
        value: "%SHARED_REPMGR_PASSWORD%"
    links:
      - toServiceId: pgsql0
        toServicePort: 54320
        localExposedPort: 54320
        variableHost: PG0_HOST
        variablePort: PG0_PORT
        variableAddress: PG0_ADD
      - toServiceId: pgsql1
        toServicePort: 54321
        localExposedPort: 54321
        variableHost: PG1_HOST
        variablePort: PG1_PORT
        variableAddress: PG1_ADD
  - id: pgsqllb
    name: pgsql-LB
    dockerConfiguration:
      image: registry.nimeops.net/layerops-public/marketplace/clusterlb
      imageVersion: "1.0"
    countMin: 2 # 2 services deployed
    countMax: 2
    cpuLimit: 512 # MHz
    memoryLimitMiB: 512
    ports:
      - listeningPort: 5432
      - listeningPort: 3000
        loadBalancerRules:
          - publicPort: 443
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
      - SHARED_LB_STATS_PASSWORD
    environmentVariables:
      - key: POSTGRESQL_HOST_LIST
        value: "%PG0_ADD%,%PG1_ADD%,%PG2_ADD%"
      - key: POSTGRESQL_POSTGRES_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: LB_STATS_PORT
        value: "3000"
      - key: LB_STATS_PASSWORD
        value: "%SHARED_LB_STATS_PASSWORD%"
      - key: "PORT_TO_CHECK"
        value: "%PG0_PORT%"
      - key: "HOST_TO_CHECK"
        value: "%PG0_HOST%"
    links:
      - toServiceId: pgsql0
        toServicePort: 54320
        localExposedPort: 54320
        variableHost: PG0_HOST
        variablePort: PG0_PORT
        variableAddress: PG0_ADD
      - toServiceId: pgsql1
        toServicePort: 54321
        localExposedPort: 54321
        variableHost: PG1_HOST
        variablePort: PG1_PORT
        variableAddress: PG1_ADD
      - toServiceId: pgsql2
        toServicePort: 54322
        localExposedPort: 54322
        variableHost: PG2_HOST
        variablePort: PG2_PORT
        variableAddress: PG2_ADD
    sideTasks: # wait for `PG0_HOST` is running, so pgsql0 (master) service
      - dockerConfiguration:
          image: registry.nimeops.net/layerops-public/marketplace/service-checker
          imageVersion: "1.0"
        type: preStart
        isLongLived: false       
  - id: ferretdb
    name: ferretdb
    dockerConfiguration:
      image: ghcr.io/ferretdb/ferretdb
      imageVersion: "latest"
      hostname: "ferretdb"
    countMin: 1 # 1 service deployed
    countMax: 1
    cpuLimit: 512 # MHz
    memoryLimitMiB: 512
    ports:
      - listeningPort: 27017
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
    environmentVariables:
      - key: POSTGRESQL_USERNAME
        value: "postgres"
      - key: POSTGRESQL_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: POSTGRESQL_DATABASE
        value: "appdb"
      - key: "FERRETDB_POSTGRESQL_URL"
        value: "postgres://%POSTGRESQL_USERNAME%:%POSTGRESQL_PASSWORD%@%PGLB_ADD%/%POSTGRESQL_DATABASE%"
      - key: "PORT_TO_CHECK"
        value: "%PGLB_PORT%"
      - key: "HOST_TO_CHECK"
        value: "%PGLB_HOST%"
    links:
      - toServiceId: pgsqllb
        toServicePort: 5432
        localExposedPort: 5432
        variableHost: PGLB_HOST
        variablePort: PGLB_PORT
        variableAddress: PGLB_ADD
    sideTasks: # wait for `PGLB_HOST` is running, so pgsqllb service
      - dockerConfiguration:
          image: registry.nimeops.net/layerops-public/marketplace/service-checker
          imageVersion: "1.0"
        type: preStart
        isLongLived: false  
  - id: mongoexpress
    name: mongo-express
    dockerConfiguration:
      image: mongo-express
      imageVersion: "latest"
      hostname: "mongoexpress"
    countMin: 1 # 1 service deployed
    countMax: 1
    cpuLimit: 512 # MHz
    memoryLimitMiB: 512
    ports:
      - listeningPort: 8081
        loadBalancerRules:
          - publicPort: 443
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_PASSWORD
      - SHARED_ME_CONFIG_BASICAUTH_PASSWORD
    environmentVariables:
      - key: ME_CONFIG_MONGODB_URL
        value: "mongodb://%DB_ADD%"
      - key: ME_CONFIG_BASICAUTH_PASSWORD
        value: "%SHARED_ME_CONFIG_BASICAUTH_PASSWORD%"
      - key: ME_CONFIG_BASICAUTH_USERNAME
        value: "admin" # user used with htaccess
      - key: "PORT_TO_CHECK"
        value: "%DB_PORT%"
      - key: "HOST_TO_CHECK"
        value: "%DB_HOST%"
    links:
      - toServiceId: ferretdb
        toServicePort: 27017
        localExposedPort: 27017
        variableHost: DB_HOST
        variablePort: DB_PORT
        variableAddress: DB_ADD
    sideTasks: # wait for `DB_HOST` is running, so ferretdb service
      - dockerConfiguration:
          image: registry.nimeops.net/layerops-public/marketplace/service-checker
          imageVersion: "1.0"
        type: preStart
        isLongLived: false  
Kubernetes environment
services:
  - id: pgsql0
    name: pgsql-0
    dockerConfiguration:
      image: bitnami/postgresql-repmgr
      imageVersion: "latest"
      hostname: "pgsql-0" # must ended by -"Number"
    countMin: 1 # 1 service deployed
    countMax: 1 
    cpuLimit: 1000 # = 1 CPU
    memoryLimitMiB: 512
    ports:
      - listeningPort: 5432
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
      - SHARED_POSTGRESQL_PASSWORD
      - SHARED_REPMGR_PASSWORD
    environmentVariables:
      - key: REPMGR_NODE_NAME
        value: "pgsql-0" # same as hostname
      - key: REPMGR_PRIMARY_HOST
        value: "pgsql-0"
      - key: REPMGR_PARTNER_NODES
        value: "%PG0_ADD%,%PG1_ADD%,%PG2_ADD%"
      - key: REPMGR_NODE_NETWORK_NAME
        value: "pgsql-0"
      - key: POSTGRESQL_POSTGRES_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: POSTGRESQL_USERNAME # user to create
        value: "appuser"
      - key: POSTGRESQL_PASSWORD
        value: "%SHARED_POSTGRESQL_PASSWORD%"
      - key: POSTGRESQL_DATABASE # database to create
        value: "appdb"
      - key: REPMGR_USERNAME
        value: "repmgr"
      - key: REPMGR_PASSWORD
        value: "%SHARED_REPMGR_PASSWORD%"
    links:
      - toServiceId: pgsql0
        toServicePort: 5432
        variableHost: PG0_HOST
        variablePort: PG0_PORT
        variableAddress: PG0_ADD
      - toServiceId: pgsql1
        toServicePort: 5432
        variableHost: PG1_HOST
        variablePort: PG1_PORT
        variableAddress: PG1_ADD
      - toServiceId: pgsql2
        toServicePort: 5432
        variableHost: PG2_HOST
        variablePort: PG2_PORT
        variableAddress: PG2_ADD
  - id: pgsql1
    name: pgsql-1
    dockerConfiguration:
      image: bitnami/postgresql-repmgr
      imageVersion: "latest"
      hostname: "pgsql-1" # must ended by -"Number"
    countMin: 1 # 1 service deployed
    countMax: 1
    cpuLimit: 1000 # = 1 CPU
    memoryLimitMiB: 512
    ports:
      - listeningPort: 5432
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
      - SHARED_POSTGRESQL_PASSWORD
      - SHARED_REPMGR_PASSWORD
    environmentVariables:
      - key: REPMGR_NODE_NAME
        value: "pgsql-1"  # same as hostname
      - key: REPMGR_PRIMARY_HOST
        value: "pgsql-0"
      - key: REPMGR_PRIMARY_PORT
        value: "%PG0_PORT%"
      - key: REPMGR_PARTNER_NODES
        value: "%PG0_ADD%,%PG1_ADD%,%PG2_ADD%"
      - key: REPMGR_NODE_NETWORK_NAME
        value: "pgsql-1"
      - key: POSTGRESQL_POSTGRES_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: POSTGRESQL_USERNAME
        value: "appuser"
      - key: POSTGRESQL_PASSWORD
        value: "%SHARED_POSTGRESQL_PASSWORD%"
      - key: POSTGRESQL_DATABASE
        value: "appdb"
      - key: REPMGR_USERNAME
        value: "repmgr"
      - key: REPMGR_PASSWORD
        value: "%SHARED_REPMGR_PASSWORD%"
    links:
      - toServiceId: pgsql0
        toServicePort: 5432
        variableHost: PG0_HOST
        variablePort: PG0_PORT
        variableAddress: PG0_ADD
      - toServiceId: pgsql1
        toServicePort: 5432
        variableHost: PG1_HOST
        variablePort: PG1_PORT
        variableAddress: PG1_ADD
      - toServiceId: pgsql2
        toServicePort: 5432
        variableHost: PG2_HOST
        variablePort: PG2_PORT
        variableAddress: PG2_ADD
  - id: pgsql2
    name: pgsql-2
    dockerConfiguration:
      image: bitnami/postgresql-repmgr
      imageVersion: "latest"
      hostname: "pgsql-2" # must ended by -"Number"
    countMin: 1 # 1 service deployed
    countMax: 1
    cpuLimit: 1000 # = 1 CPU
    memoryLimitMiB: 512
    ports:
      - listeningPort: 5432
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
      - SHARED_POSTGRESQL_PASSWORD
      - SHARED_REPMGR_PASSWORD
    environmentVariables:
      - key: REPMGR_NODE_NAME
        value: "pgsql-2"  # same as hostname
      - key: REPMGR_PRIMARY_HOST
        value: "pgsql-0"
      - key: REPMGR_PRIMARY_PORT
        value: "%PG0_PORT%"
      - key: REPMGR_PARTNER_NODES
        value: "%PG0_ADD%,%PG1_ADD%,%PG1_ADD%"
      - key: REPMGR_NODE_NETWORK_NAME
        value: "pgsql-2"
      - key: POSTGRESQL_POSTGRES_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: POSTGRESQL_USERNAME
        value: "appuser"
      - key: POSTGRESQL_PASSWORD
        value: "%SHARED_POSTGRESQL_PASSWORD%"
      - key: POSTGRESQL_DATABASE
        value: "appdb"
      - key: REPMGR_USERNAME
        value: "repmgr"
      - key: REPMGR_PASSWORD
        value: "%SHARED_REPMGR_PASSWORD%"
    links:
      - toServiceId: pgsql0
        toServicePort: 5432
        variableHost: PG0_HOST
        variablePort: PG0_PORT
        variableAddress: PG0_ADD
      - toServiceId: pgsql1
        toServicePort: 5432
        variableHost: PG1_HOST
        variablePort: PG1_PORT
        variableAddress: PG1_ADD
      - toServiceId: pgsql2
        toServicePort: 5432
        variableHost: PG2_HOST
        variablePort: PG2_PORT
        variableAddress: PG2_ADD
  - id: pgsqllb
    name: pgsql-LB
    dockerConfiguration:
      image: registry.nimeops.net/layerops-public/marketplace/clusterlb
      imageVersion: "1.0"
    countMin: 2 # 2 services deployed
    countMax: 2
    cpuLimit: 1000 # = 1 CPU
    memoryLimitMiB: 512
    ports:
      - listeningPort: 5432
      - listeningPort: 3000
        loadBalancerRules:
          - publicPort: 443
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
      - SHARED_LB_STATS_PASSWORD
    environmentVariables:
      - key: POSTGRESQL_HOST_LIST
        value: "%PG0_ADD%,%PG1_ADD%,%PG2_ADD%"
      - key: POSTGRESQL_POSTGRES_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: LB_STATS_PORT
        value: "3000"
      - key: LB_STATS_PASSWORD
        value: "%SHARED_LB_STATS_PASSWORD%"
      - key: "PORT_TO_CHECK"
        value: "%PG0_PORT%"
      - key: "HOST_TO_CHECK"
        value: "%PG0_HOST%"
    links:
      - toServiceId: pgsql0
        toServicePort: 5432
        variableHost: PG0_HOST
        variablePort: PG0_PORT
        variableAddress: PG0_ADD
      - toServiceId: pgsql1
        toServicePort: 5432
        variableHost: PG1_HOST
        variablePort: PG1_PORT
        variableAddress: PG1_ADD
      - toServiceId: pgsql2
        toServicePort: 5432
        variableHost: PG2_HOST
        variablePort: PG2_PORT
        variableAddress: PG2_ADD
    sideTasks: # wait for `PG0_HOST` is running, so pgsql0 (master) service
      - dockerConfiguration:
          image: registry.nimeops.net/layerops-public/marketplace/service-checker
          imageVersion: "1.0"
        type: preStart
        isLongLived: false       
  - id: ferretdb
    name: ferretdb
    dockerConfiguration:
      image: ghcr.io/ferretdb/ferretdb
      imageVersion: "latest"
      hostname: "ferretdb"
    countMin: 1 # 1 service deployed
    countMax: 1
    cpuLimit: 1000 # = 1 CPU
    memoryLimitMiB: 512
    ports:
      - listeningPort: 27017
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_POSTGRES_PASSWORD
    environmentVariables:
      - key: POSTGRESQL_USERNAME
        value: "postgres"
      - key: POSTGRESQL_PASSWORD
        value: "%SHARED_POSTGRESQL_POSTGRES_PASSWORD%"
      - key: POSTGRESQL_DATABASE
        value: "appdb"
      - key: "FERRETDB_POSTGRESQL_URL"
        value: "postgres://%POSTGRESQL_USERNAME%:%POSTGRESQL_PASSWORD%@%PGLB_ADD%/%POSTGRESQL_DATABASE%"
      - key: "PORT_TO_CHECK"
        value: "%PGLB_PORT%"
      - key: "HOST_TO_CHECK"
        value: "%PGLB_HOST%"
    links:
      - toServiceId: pgsqllb
        toServicePort: 5432
        localExposedPort: 5432
        variableHost: PGLB_HOST
        variablePort: PGLB_PORT
        variableAddress: PGLB_ADD
    sideTasks: # wait for `PGLB_HOST` is running, so pgsqllb service
      - dockerConfiguration:
          image: registry.nimeops.net/layerops-public/marketplace/service-checker
          imageVersion: "1.0"
        type: preStart
        isLongLived: false  
  - id: mongoexpress
    name: mongo-express
    dockerConfiguration:
      image: mongo-express
      imageVersion: "latest"
      hostname: "mongoexpress"
    countMin: 1 # 1 service deployed
    countMax: 1
    cpuLimit: 1000 # = 1 CPU
    memoryLimitMiB: 512
    ports:
      - listeningPort: 8081
        loadBalancerRules:
          - publicPort: 443
    sharedEnvironmentVariables:
      - SHARED_POSTGRESQL_PASSWORD
      - SHARED_ME_CONFIG_BASICAUTH_PASSWORD
    environmentVariables:
      - key: ME_CONFIG_MONGODB_URL
        value: "mongodb://%DB_ADD%"
      - key: ME_CONFIG_BASICAUTH_PASSWORD
        value: "%SHARED_ME_CONFIG_BASICAUTH_PASSWORD%"
      - key: ME_CONFIG_BASICAUTH_USERNAME
        value: "admin" # user used with htaccess
      - key: "PORT_TO_CHECK"
        value: "%DB_PORT%"
      - key: "HOST_TO_CHECK"
        value: "%DB_HOST%"
    links:
      - toServiceId: ferretdb
        toServicePort: 27017
        localExposedPort: 27017
        variableHost: DB_HOST
        variablePort: DB_PORT
        variableAddress: DB_ADD
    sideTasks: # wait for `DB_HOST` is running, so ferretdb service
      - dockerConfiguration:
          image: registry.nimeops.net/layerops-public/marketplace/service-checker
          imageVersion: "1.0"
        type: preStart
        isLongLived: false