RDS PostgreSQL

Based on the RDS PostgreSQL docker image this Terraform module provides a ready to use PostgreSQL server that is backed up to a S3 compatible object store.

The module supports two backup methods either local storage (Hetzner Cloud Volume) or a S3 compatible object store. To avoid data loss it is not possible to run the database without one of the backup methods enabled.

Design Goals

Acknowledging that there are a lot of available solutions to run PostgreSQL in the cloud it is important to emphasize the design goals of this solution to be able to make an informed decision on if and how to use it

  • First and foremost goal is to preserve the data stored in the PostgreSQL instance. This is ensured by an extensive set of full integration tests replicating the intended usage scenarios
  • Reduced complexity through single node operations. Running a PostgreSQL server with replication or in multi master comes with its own set of diverse failure scenarios and edge-cases. This solution aims at workloads that can easily handled by a single node and if in doubt favours MTTR over MTBF
  • Reduced complexity through less moving parts. Features like autoscaling, automatic recovery after zone outages, user interfaces, etc. like they are provided by the Zalando PostgreSQL Operator provide a lot of value but also add multiple layers between the VM and the actual PostgreSQL instance. This solutions explicitly tries to avoid these features trying to keep the stack as simple as running a docker container on a VM.

Usage

data "aws_s3_bucket" "backup" {
  bucket = "test-rds-postgresql-backup"
}

data "hcloud_volume" "data" {
  name = "rds-postgresql-data"
}

resource "tls_private_key" "ssh_key" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "hcloud_ssh_key" "ssh_key" {
  name       = "rds-postgresql"
  public_key = tls_private_key.ssh_key.public_key_openssh
}

module "rds-postgresql" {
  source = "https://github.com/pellepelster/solidblocks/releases/download/v0.4.12/blcks-terraform-rds-postgresql-hetzner-v0.4.12.zip"

  name     = "rds-postgresql"
  location = var.hetzner_location

  ssh_keys = [hcloud_ssh_key.ssh_key.id]

  data_volume = data.hcloud_volume.data.id

  backup_s3_bucket     = data.aws_s3_bucket.backup.id
  backup_s3_access_key = var.backup_s3_access_key
  backup_s3_secret_key = var.backup_s3_secret_key

  databases = [
    { id : "database1", user : "user1", password : "password1" }
  ]
}

For more usage examples see Extended Usage

Operations

To access the created instance use one of SSH keys that was provided via ssh_keys. All linux system resources include the provided name to make them distinguishable.

Show Logs

journalctl -u rds@rds-postgresql-${name}.service

Start/Stop Database

systemctl [start|stop] rds@rds-postgresql-${name}.service 

Maintenance Mode

In case of errors or the need for manual intervention a maintenance mode is available. Triggering the maintenance mode will set up the VM like it would be set up for database startup, but without actually starting the database. This allows to exec into the container to debug issues.

main.tf

module "rds-postgresql" {
  source  = "pellepelster/solidblocks-rds-postgresql/hcloud"
  # [...]
  mode = "maintenance"
  # [...]
}
docker exec -ti rds-postgresql-${name}_postgresql /bin/bash

Terraform Module

Requirements

NameVersion
hcloud>=1.48.0
http>= 3.3.0

Providers

NameVersion
hcloud>=1.48.0

Resources

NameType
hcloud_server.rdsresource
hcloud_volume_attachment.backupresource
hcloud_volume_attachment.dataresource
hcloud_volume.backupdata source
hcloud_volume.datadata source

Inputs

NameDescriptionTypeDefaultRequired
backup_encryption_passphraseIf set the backups will be encrypted using this passphrasestringnullno
backup_full_calendarsystemd timer spec for full backupsstring"*-*-* 20:00:00"no
backup_incr_calendarsystemd timer spec for incremental backupsstring"*-*-* *:00:55"no
backup_local_retention_diffLocal backup number of differential backups to retain. See https://pgbackrest.org/configuration.html#section-repository/option-repo-retention-diffnumber4no
backup_local_retention_fullLocal backups full backup retention count/time. See https://pgbackrest.org/configuration.html#section-repository/option-repo-retention-fullnumber7no
backup_local_retention_full_typeLocal backups retention policy type [count, time]. See https://pgbackrest.org/configuration.html#section-repository/option-repo-retention-fullstring"count"no
backup_s3_access_keyAWS access key for S3 backups. To enable S3 backups backup_s3_bucket, backup_s3_access_key and backup_s3_secret_key have to be provided.stringnullno
backup_s3_bucketAWS bucket name for S3 backups. To enable S3 backups backup_s3_bucket, backup_s3_access_key and backup_s3_secret_key have to be provided.stringnullno
backup_s3_hostAWS host S3 backups.string"s3.eu-central-1.amazonaws.com"no
backup_s3_regionAWS region for S3 backups.string"eu-central-1"no
backup_s3_retention_diffAWS S3 backup number of differential backups to retain. See https://pgbackrest.org/configuration.html#section-repository/option-repo-retention-diffnumber4no
backup_s3_retention_fullAWS S3 backups full backup retention count/time. See https://pgbackrest.org/configuration.html#section-repository/option-repo-retention-fullnumber7no
backup_s3_retention_full_typeAWS S3 backups retention policy type [count, time]. See https://pgbackrest.org/configuration.html#section-repository/option-repo-retention-fullstring"count"no
backup_s3_secret_keyAWS secret key for S3 backups. To enable S3 backups backup_s3_bucket backup_s3_access_key and backup_s3_secret_key have to be provided.stringnullno
backup_volumebackup volume idstring0no
data_volumedata volume idnumbern/ayes
databasesA list of databases to create when the instance is initialized, for example: { id : "database1", user : "user1", password : "password1" }. Changing user and password is supported at any time, the provided config is translated into an config for the Solidblocks RDS PostgreSQL module (https://pellepelster.github.io/solidblocks/rds/index.html), please see https://pellepelster.github.io/solidblocks/rds/index.html#databases for more details of the database configuration.list(object({ id : string, user : string, password : string }))n/ayes
db_admin_passwordThe database admin password. Username is always rdsstring""no
db_backup_gcs_bucketName of the Google Cloud storage bucketstringnullno
db_backup_gcs_service_keycontent of the service key json file with appropriate permissions to write to the db_backup_gcs_bucket bucket.stringnullno
environment_variablesA list environment variables to pass to the PostgreSQL docker containermap(string){}no
extra_user_datadeprecated, please use pre_script/post_scriptstring""no
firewall_disabledisable automatic firewall configurationboolfalseno
labelsA list of labels to be attached to the server instance.map(any){}no
locationHetzner location to use for provisioned resourcesstringn/ayes
modestartup mode for the database, can be empty to start the database or ‘maintenance’ to enable the maintenance mode of the underlying docker container to debug issues see also https://pellepelster.github.io/solidblocks//rds/#maintenancestringnullno
nameUnique name for the PostgreSQL instancestringn/ayes
network_idnetwork the created sever should be attached to, network_ip also needs to bet set in that casenumbernullno
network_ipip address in the attached network. when an ip address is provided the database server will automatically be bound to this ip and will not be exposed on any other network interfacesstringnullno
post_scriptshell script that will be executed after the server configuration is executedstring""no
postgres_extra_configExtra postgres configurations options for the postgresql.conf, see also https://pellepelster.github.io/solidblocks/rds/index.html#global -> DB_POSTGRES_EXTRA_CONFIGstringnullno
postgres_major_versionPostgreSQL major version to use. Upgrading the version will trigger auto migration based on the underlying RDS PostgreSQL docker image, see also https://pellepelster.github.io/solidblocks/rds/index.html#versions. Please be aware that depending on the amount of data to migrate the migration may Terraforms timeouts, see https://pellepelster.github.io/solidblocks/hetzner/rds-postgresql/index.html#operations for debugging options.number14no
postgres_stop_timeoutshutdown timeout for the postgres database in seconds, see also https://www.postgresql.org/docs/current/app-pg-ctl.htmlnumber60no
pre_scriptshell script that will be executed before the server configuration is executedstring""no
public_net_ipv4_enabledenable/disable public ip addresses, see also https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs/resources/server#public_netbooltrueno
public_net_ipv6_enabledenable/disable public ip addresses, see also https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs/resources/server#public_netbooltrueno
restore_pitrPoint in time to recover to, using the recovery type time as defined in https://pgbackrest.org/command.html#command-restore. Format should be YYYY-MM-dd HH:mm:ssz Please be aware that the server hosting the database might be in a different timezone, so always include the timezone when specifying PITR times date +"%Y-%m-%d %H:%M:%S%z"stringnullno
server_typeHetzner cloud server type, supports x86 and ARM architecturesstring"cx23"no
solidblocks_base_urloverride base url for testing purposesstring"https://github.com"no
solidblocks_rds_versionSolidblocks rds-postgresql version to usestring"v0.4.12"no
ssh_keysssh keys to provision for instance accesslist(number)n/ayes
ssl_acme_serverThe URL of the ACME Server to use. Defaults to Let’s Encrypt production environment.string"https://acme-v02.api.letsencrypt.org/directory"no
ssl_dns_providerprovider type to use for LetsEncrypt DNS challenge, see https://go-acme.github.io/lego/dns/ for available optionsstring""no
ssl_dns_provider_configenvironment configuration variable(s) to use for DNS provider selected via ssl_dns_provider, see documentation of selected provider for required configuration variablesmap(string){}no
ssl_domainsdomains to use for generated certificateslist(string)[]no
ssl_emailemail address to use for LetsEncrypt account creationstring""no
ssl_enableenable automatic ssl certificate creation using LetsEncryptboolfalseno

Outputs

NameDescription
ipv4_addressIPv4 address of the created server if applicable
ipv4_address_privateprivate IPv4 address of the created server if applicable
ipv6_addressIPv6 address of the created server if applicable
this_server_idHetzner ID of the created server
user_datan/a