Logo kepa.eu.org

Terraform Workspaces in PostgreSQL Backend

Oct 7, 2025 - 3 minute read

This blog post explores how to set up Terraform workspaces with a PostgreSQL backend to manage infrastructure across multiple environments (dev, stage, prod). Rather than relying on AWS S3 for state storage, we’ll use PostgreSQL running in Docker, providing a lightweight local alternative that simplifies development workflows.

You’ll learn how to configure Terraform providers for AWS and PostgreSQL, create some example boilerplate code, and manage different deployment environments using workspaces. This setup is ideal for teams looking to practice infrastructure-as-code principles with flexible state management while maintaining secure, versioned state files while having terraform enterprise experience.


Project Structure

terraform-postgres-project/

├── environments/
│   ├── dev.tfvars
│   ├── stage.tfvars
│   └── prod.tfvars
├── main.tf
├── s3.tf
├── variables.tf
├── outputs.tf
└── docker-compose.yml

main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
    postgresql = {
      source  = "cyrilgdn/postgresql"
      version = "~> 1.21"
    }
  }

  backend "pg" {
    conn_str = "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable"
  }
}

provider "aws" {
  region = var.region
}

provider "postgresql" {
  host     = "localhost"
  port     = 5432
  database = "postgres"
  username = "postgres"
  password = "postgres"
  sslmode  = "disable"
}

s3.tf

resource "aws_s3_bucket" "terraform_state" {
  bucket = "my-terraform-state-bucket-${var.environment}"

  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_s3_bucket_versioning" "versioning" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" {
  bucket = aws_s3_bucket.terraform_state.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

variables.tf

variable "region" {
  description = "AWS Region"
  type        = string
  default     = "us-east-1"
}

variable "environment" {
  description = "Deployment environment"
  type        = string
  default     = "dev"
}

init-postgres.sh

#!/bin/bash
# Ensure PostgreSQL configuration is updated
postgresql_conf="/var/lib/postgresql/data/postgresql.conf"
pghba_conf="/var/lib/postgresql/data/pg_hba.conf"

# Update postgresql.conf to listen on all addresses
sed -i "s|^#*listen_addresses[[:space:]]*=.*|listen_addresses = '*'|" $postgresql_conf

# Update pg_hba.conf to allow connections from all IPs
sed -i "s|127.0.0.1/32|0.0.0.0/0|g" $pghba_conf
sed -i "s|::1/128|::/0|g" $pghba_conf

cat <<EOL >> $pghba_conf
# Allow all IPv4 connections
host    all             all             0.0.0.0/0               md5
# Allow all IPv6 connections
host    all             all             ::1/128                md5
EOL

# Restart PostgreSQL service to apply changes
pg_ctl -D /var/lib/postgresql/data -m fast restart

docker-compose.yml

services:
  postgres:
    image: postgres:17
    environment:
      POSTGRES_DB: postgres
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - 5432:5432
    volumes:
      - ./init-postgres.sh:/docker-entrypoint-initdb.d/init-postgres.sh
      - postgres_data:/var/lib/postgresql/data
    extra_hosts:
      - 'host.docker.internal:172.17.0.1'

  pgadmin:
    image: dpage/pgadmin4
    container_name: pgadmin4
    environment:
      PGADMIN_DEFAULT_EMAIL: 'admin@local.host'
      PGADMIN_DEFAULT_PASSWORD: 'admin'
    ports:
      - 5050:80
    volumes:
      - pgadmin_data:/var/lib/pgadmin
    depends_on:
      - postgres
    extra_hosts:
      - 'host.docker.internal:172.17.0.1'

volumes:
  postgres_data:
  pgadmin_data:

Setup and initialization

# Start PostgreSQL container
docker-compose up -d

# Initialize Terraform
terraform init

# Create S3 Bucket and Configure State Backend
terraform workspace new dev
terraform workspace select dev
terraform plan
terraform apply -var="environment=dev"

# Switch between environments
terraform workspace select stage
terraform apply -var="environment=stage"

terraform workspace select prod
terraform apply -var="environment=prod"

Check inside the pgAdmin

  • Navigate to http://localhost:5050/browser/
  • Login with credentials from compose.yaml file
  • Right Click at the database and select “Query Tool”
  • Try below screen

Key Considerations

  • State Security: The PostgreSQL backend stores state securely
  • Multi-Environment Support: Uses Terraform workspaces for different environments
  • Local PostgreSQL Backend: Easily manageable with Docker Compose