This repository provides a secure and minimal Docker image for Tailscale's pgproxy, built using Chainguard's hardened, distroless Golang base image with a multi-stage build process.
Tailscale's pgproxy.go was designed internally and recently open-sourced, enabling secure, identity-aware access to PostgreSQL instances over Tailscale mTLS. This implementation focuses on reducing the attack surface by using a minimal container image and following container hardening best practices.
This repository offers two deployment methods:
- π³ Docker Compose - Local development and testing
- βοΈ AWS Terraform - Production-ready cloud infrastructure
Choose the deployment method that best fits your needs:
- Use Docker Compose for local development, testing, or simple deployments
- Use AWS Terraform for production environments with auto-scaling, managed databases, and enterprise security features
Perfect for local development and testing environments.
- Docker and Docker Compose installed
- Tailscale account and access to admin panel
- SSL/TLS certificates for PostgreSQL (including CA)
- AWS CLI configured with appropriate permissions
- Terraform >= 1.0 installed
- Tailscale account and API key
Make sure you have the necessary certificates in the certificates/ directory:
certificates/
βββ ca.pem # Certificate Authority certificate
βββ server.crt # PostgreSQL server certificate
βββ server.key # PostgreSQL server private keyCreate a .env file in the project root with the following variables:
# PostgreSQL configuration
POSTGRES_USER=your_username
POSTGRES_PASSWORD=your_secure_password
# Tailscale API Key
TSKEY_API=tskey-api-xxxxxx-xxxxxxxxxxxxxxxxxTo obtain the TSKEY_API:
- Access the Tailscale admin panel
- Go to Settings β Keys
- Generate a new Auth Key with appropriate permissions
If you want to build the image locally instead of using the pre-built image:
# Build the image
docker build -t pgproxy:local .
# Update docker-compose.yaml to use the local image
# Change: image: rafaelribeiro96/pgproxy:1.82.5
# To: image: pgproxy:localRun the complete stack with Docker Compose:
# Start the services
docker-compose up -d
# Check if containers are running
docker-compose ps
# Check pgproxy logs
docker-compose logs -f pgproxy
# Check PostgreSQL logs
docker-compose logs -f postgresAfter starting the services, verify everything is working:
# Check if pgproxy is listening on port 5432
netstat -tulpn | grep :5432
# Test local connection (if needed)
docker-compose exec postgres psql -U your_username -d test_dbOnce pgproxy is running and connected to Tailscale:
- Connect your client to the same Tailnet
- Use the hostname
pgproxy-nodeto connect to PostgreSQL - The connection will be automatically authenticated via Tailscale mTLS
Connection example:
psql -h pgproxy-node -p 5432 -U your_username -d test_dbTo change the Tailscale hostname, edit the docker-compose.yaml:
command:
[
"--hostname", "my-custom-pgproxy",
# ... other parameters
]The project uses a custom bridge network (pgnet) for isolation. Containers communicate internally through this network.
pgdata: PostgreSQL data./pgproxy-state: Tailscale authentication state
- Certificate error: Verify that certificates are in the correct format and have adequate permissions
- Tailscale authentication failure: Check if TSKEY_API is valid and has necessary permissions
- Connection refused: Make sure both containers are on the same network
# Detailed pgproxy logs
docker-compose logs --tail=100 -f pgproxy
# PostgreSQL logs
docker-compose logs --tail=100 -f postgres
# Container status
docker-compose ps- This setup uses
runscruntime (gVisor) for additional isolation - Certificates are mounted as read-only
- PostgreSQL runs with non-root user (uid:gid 70:70)
- Base images are hardened and distroless from Chainguard
For production environments with enterprise-grade security, auto-scaling, and managed services.
The Terraform configuration deploys:
- AWS Resource Group: Organizes all resources for easy management
- Aurora Serverless v2: Auto-scaling PostgreSQL database (0.5-16 ACUs)
- AWS Lambda: Function for pgproxy integration and health monitoring
- VPC & Security Groups: Network isolation and security controls
- AWS Secrets Manager: Secure storage for credentials and API keys
- KMS Encryption: Encryption at rest for all secrets
- CloudWatch: Comprehensive logging and monitoring
-
Navigate to Terraform directory:
cd terraform -
Configure variables:
# Copy example configuration cp terraform.tfvars.example terraform.tfvars # Edit with your specific values nano terraform.tfvars
-
Set sensitive variables:
export TF_VAR_tailscale_auth_key="tskey-api-xxxxxx-xxxxxxxxxxxxxxxxx" export TF_VAR_database_password_override="your-secure-password"
-
Deploy infrastructure:
# Initialize Terraform terraform init # Review planned changes terraform plan # Deploy infrastructure terraform apply
-
Verify deployment:
# View all outputs terraform output # Test Lambda function aws lambda invoke \ --function-name $(terraform output -raw lambda_function_name) \ --payload '{"action": "health_check"}' \ response.json && cat response.json
Create new VPC (default):
create_vpc = true
vpc_cidr = "10.0.0.0/16"Use existing VPC:
create_vpc = false
existing_vpc_id = "vpc-xxxxxxxxx"
existing_subnet_ids = ["subnet-xxxxxxxxx", "subnet-yyyyyyyyy"]aurora_min_capacity = 0.5 # Minimum ACUs (scales to zero cost when idle)
aurora_max_capacity = 16 # Maximum ACUs for peak loadslambda_memory_size = 128 # Memory in MB
lambda_timeout = 30 # Timeout in seconds- Secrets Manager Integration: All credentials encrypted and managed
- VPC Isolation: Database in private subnets only
- Security Groups: Restrictive network access rules
- KMS Encryption: Custom encryption keys for secrets
- IAM Least Privilege: Minimal required permissions
- Performance Insights: Database monitoring and optimization
- Aurora Serverless v2: Automatically scales down to 0.5 ACU when idle
- Lambda: Pay only for execution time
- Configurable log retention: Control CloudWatch costs
- Resource tagging: Track costs by environment/project
- CloudWatch Logs: Centralized logging for Lambda and Aurora
- Performance Insights: Database performance monitoring
- Custom metrics: Application-specific monitoring
- Tagging strategy: Organized resource management
- Lambda VPC timeout: Ensure NAT Gateway or VPC endpoints for AWS services
- Database connection errors: Verify security groups and subnet configuration
- Secrets access denied: Check IAM permissions for Lambda execution role
- Terraform state issues: Use remote state backend for team environments
# Check Aurora cluster status
aws rds describe-db-clusters \
--db-cluster-identifier $(terraform output -raw aurora_cluster_id)
# View Lambda logs
aws logs tail /aws/lambda/$(terraform output -raw lambda_function_name) --follow
# Test database connectivity
aws lambda invoke \
--function-name $(terraform output -raw lambda_function_name) \
--payload '{"action": "database_test"}' \
response.json && cat response.json
# Check resource costs
aws ce get-cost-and-usage \
--time-period Start=2024-01-01,End=2024-01-31 \
--granularity MONTHLY \
--metrics BlendedCostterraform/
βββ main.tf # Main infrastructure configuration
βββ variables.tf # Input variables and validation
βββ vault.tf # Secrets Manager and KMS configuration
βββ outputs.tf # Resource outputs and connection info
βββ terraform.tfvars.example # Configuration template
βββ build_lambda.sh # Lambda deployment package builder
βββ .gitignore # Terraform-specific ignore rules
βββ README.md # Detailed Terraform documentation
βββ lambda/
βββ lambda_function.py # Lambda function implementation
βββ requirements.txt # Python dependencies
For production deployments, consider:
- Remote State Backend: Use S3 + DynamoDB for state management
- Multi-Environment: Separate workspaces for dev/staging/prod
- CI/CD Integration: Automate deployments with GitHub Actions/GitLab CI
- Backup Strategy: Enable automated backups and point-in-time recovery
- Monitoring Alerts: Set up CloudWatch alarms for critical metrics
- Security Scanning: Regular vulnerability scans and compliance checks
- AWS Aurora Serverless v2 Documentation
- AWS Lambda Documentation
- AWS Secrets Manager Documentation
- Terraform AWS Provider
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly (both Docker and Terraform deployments)
- Submit a pull request
This project follows the same license as the original Tailscale pgproxy implementation.
- Docker Issues: Check container logs and Docker Compose configuration
- AWS Issues: Review CloudWatch logs and AWS resource status
- Terraform Issues: Verify configuration and check Terraform state
- General Questions: Open an issue in this repository