Deploy a production-ready PostgreSQL 16 server with enterprise-grade security defaults. This guide covers localhost binding, firewall configuration, and least-privilege database roles on Massed Compute infrastructure.
Skip the manual setup. This guide exists as a tested, machine-readable recipe in the Massed Compute MCP. Connect an AI agent to provision and configure your PostgreSQL server automatically.
PostgreSQL remains the world’s most advanced open-source database, but default installations often lack production-grade security. This guide deploys PostgreSQL 16 with defense-in-depth: localhost-only binding, UFW firewall protection, revoked public schema access, and a non-superuser application role.
You’ll provision a secure database server that follows PostgreSQL security best practices from day one, eliminating common attack vectors while maintaining full functionality for your applications.
| Component | Version | Purpose |
|---|---|---|
| PostgreSQL | 16.x | Primary database engine |
| Ubuntu Server | 24.04 LTS | Base operating system |
| UFW | Latest | Uncomplicated firewall |
| Massed Compute | API v1 | Cloud infrastructure |
| Resource | Minimum | Recommended | Notes |
|---|---|---|---|
| vCPU | 2 cores | 4+ cores | For moderate workloads |
| RAM | 4 GiB | 8+ GiB | PostgreSQL shared_buffers tuning |
| Storage | 400 GB | 800+ GB | SSD recommended for IOPS |
| Network | SSH access | SSH + private network | Secure administration |
Massed Compute VM Pricing
Choose a CPU-optimized VM that meets your database workload requirements. PostgreSQL benefits from additional CPU cores and RAM for concurrent connections and query processing.
| SKU | Description | vCPU | RAM | Storage | Price | Capacity |
|---|---|---|---|---|---|---|
cpu_mini_amd_epyc |
Mini AMD EPYC | 8 | 32 GiB | 400 GB | $0.12/hr | 31 |
cpu_small_amd_epyc |
Small AMD EPYC | 14 | 40 GiB | 800 GB | $0.22/hr | 31 |
cpu_medium_amd_epyc |
Medium AMD EPYC | 28 | 80 GiB | 1600 GB | $0.44/hr | 17 |
cpu_large_amd_epyc |
Large AMD EPYC | 52 | 160 GiB | 3200 GB | $0.82/hr | 7 |
cpu_x_large_amd_epyc |
X-Large AMD EPYC | 100 | 320 GiB | 6400 GB | $1.56/hr | 3 |
cpu_dedicated_amd_epyc |
Dedicated AMD EPYC | 126 | 440 GiB | 10000 GB | $1.98/hr | 2 |
cpu_mini_amd_epyc provides excellent value for development and small production databases. Scale up to cpu_medium_amd_epyc or larger for high-throughput workloads.
Step-by-Step Deployment
Provision Your VM
Launch a new VM in the Massed Compute dashboard or via API:
- Image: Ubuntu Server 24.04 LTS
- Product:
cpu_mini_amd_epycor larger CPU instance - Region: Any region with available capacity
- SSH key: Attach a key you can access from your workstation
Wait until the VM status shows Running and SSH is reachable on port 22.
Set Database Credentials
Define your database configuration. Replace these placeholders with your actual values:
YOUR_SSH_USER: SSH username (usuallyubuntu)YOUR_VM_IP: Public IP of your VMYOUR_SSH_KEY_PATH: Path to your SSH private keyyour_app_db: Database name for your applicationapp_user: Non-superuser database rolesecure_password_16_chars: Strong password (16+ characters)
Install and Configure PostgreSQL
Run this command from your workstation to install PostgreSQL with security hardening:
ssh -i YOUR_SSH_KEY_PATH YOUR_SSH_USER@YOUR_VM_IP 'bash -s' <<'REMOTE'
set -euo pipefail
DB_NAME='your_app_db'
DB_USER='app_user'
DB_PASSWORD='secure_password_16_chars'
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update
sudo apt-get install -y postgresql postgresql-contrib ufw
# Configure UFW firewall
sudo ufw allow OpenSSH
sudo ufw --force enable
# Configure PostgreSQL for localhost-only access
PG_CONF=$(sudo -u postgres psql -Atc "SHOW config_file;")
sudo sed -i "s/^#\?listen_addresses.*/listen_addresses = 'localhost'/" "$PG_CONF"
sudo systemctl enable --now postgresql
sudo systemctl restart postgresql
echo POSTGRESQL_INSTALL_OK
REMOTE
Create Database and User
Set up your application database with a least-privilege user role:
ssh -i YOUR_SSH_KEY_PATH YOUR_SSH_USER@YOUR_VM_IP 'bash -s' <<'REMOTE'
set -euo pipefail
DB_NAME='your_app_db'
DB_USER='app_user'
DB_PASSWORD='secure_password_16_chars'
sudo -u postgres psql <-- Create application role
DO \$\$ BEGIN
CREATE ROLE $DB_USER LOGIN PASSWORD '$DB_PASSWORD';
EXCEPTION WHEN duplicate_object THEN
ALTER ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASSWORD';
END \$\$;
-- Create application database
SELECT 'CREATE DATABASE $DB_NAME OWNER $DB_USER'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '$DB_NAME')\gexec
-- Revoke public access and grant to app user
REVOKE ALL ON DATABASE $DB_NAME FROM PUBLIC;
\c $DB_NAME
REVOKE ALL ON SCHEMA public FROM PUBLIC;
GRANT USAGE, CREATE ON SCHEMA public TO $DB_USER;
SQL
echo DATABASE_SETUP_OK
REMOTE
Verify Installation
Test the database connection and security configuration:
ssh -i YOUR_SSH_KEY_PATH YOUR_SSH_USER@YOUR_VM_IP 'bash -s' <<'REMOTE'
set -euo pipefail
DB_NAME='your_app_db'
DB_USER='app_user'
DB_PASSWORD='secure_password_16_chars'
# Test application database connection
PGPASSWORD="$DB_PASSWORD" psql -h 127.0.0.1 -U "$DB_USER" -d "$DB_NAME" -c \
"CREATE TABLE IF NOT EXISTS recipe_smoke(id int primary key);
INSERT INTO recipe_smoke VALUES (1) ON CONFLICT DO NOTHING;
SELECT count(*) FROM recipe_smoke;"
# Verify non-superuser role
if sudo -u postgres psql -Atc "SELECT rolsuper FROM pg_roles WHERE rolname='$DB_USER';" | grep -q '^t$'; then
echo "PostgreSQL app role must not be a superuser" >&2
exit 1
fi
# Check service and firewall status
sudo systemctl is-active --quiet postgresql
sudo ufw status | grep -q '^Status: active'
sudo ss -ltnp | grep -E '127\.0\.0\.1:5432|\[::1\]:5432'
echo POSTGRESQL_VERIFICATION_OK
REMOTE
Troubleshooting
Connection Refused
If you see psql: connection refused, check the PostgreSQL service status and configuration:
sudo systemctl status postgresql
sudo grep listen_addresses /etc/postgresql/*/main/postgresql.conf
Ensure listen_addresses = 'localhost' is set and PostgreSQL has restarted.
Authentication Failure
Reset the application role password if authentication fails:
sudo -u postgres psql -c "ALTER ROLE app_user WITH PASSWORD 'new_secure_password';"
REMOTE
Remote Access Requirements
For production applications, prefer application proxies or private networks over exposing PostgreSQL directly. If you must allow remote connections, add specific CIDR blocks to pg_hba.conf and create matching UFW rules.
Skip All of This: Deploy with an AI Agent
This entire guide exists as a tested, machine-readable recipe in the Massed Compute MCP. Recipe last tested: June 10, 2026.
Add this MCP server to your AI client:
{
"mcpServers": {
"massed-compute": {
"type": "http",
"url": "https://vm.massedcompute.com/api/mcp",
"headers": { "Authorization": "Bearer MC_TOKEN" }
}
}
}
Then say:
The agent matches your request against the recipe catalog, provisions a cpu_mini_amd_epyc VM, runs the installation and security hardening steps above, creates your specified database and user, and reports back with connection details. If any step fails, the agent stops and reports the exact error.
Quick Setup Guide
For experienced developers who want the essential commands:
# 1. Provision Ubuntu 24.04 VM with cpu_mini_amd_epyc
# 2. SSH and run:
sudo apt-get update && sudo apt-get install -y postgresql postgresql-contrib ufw
sudo ufw allow OpenSSH && sudo ufw --force enable
# 3. Configure localhost binding
PG_CONF=$(sudo -u postgres psql -Atc "SHOW config_file;")
sudo sed -i "s/^#\?listen_addresses.*/listen_addresses = 'localhost'/" "$PG_CONF"
sudo systemctl restart postgresql
# 4. Create database and user
sudo -u postgres psql <# 5. Test connection
PGPASSWORD='secure_password' psql -h 127.0.0.1 -U app_user -d app_db -c 'SELECT version();'
Frequently Asked Questions
01Why bind PostgreSQL to localhost only?
Localhost binding prevents direct internet access to your database server, eliminating network-based attacks. Applications should connect through secure tunnels, private networks, or application proxies rather than exposing PostgreSQL directly to the internet.
02How do I connect my application to the database?
Applications running on the same VM can connect using host=127.0.0.1. For external applications, set up SSH tunneling, use private networking between VMs, or configure a connection proxy with proper authentication and encryption.
03Can I upgrade this setup to PostgreSQL 17 later?
Yes, but plan for a maintenance window. PostgreSQL major version upgrades require data migration using pg_dump and pg_restore or pg_upgrade. Test the upgrade process in a staging environment first and ensure your applications are compatible with PostgreSQL 17.
04What’s the recommended backup strategy?
Implement automated daily backups using pg_dump with compression and remote storage. For high-availability setups, consider continuous archiving with Write-Ahead Logging (WAL) shipping. Test restore procedures regularly to ensure backup integrity.
05How do I monitor PostgreSQL performance?
Enable PostgreSQL’s built-in statistics collector and use pg_stat_* views for query analysis. Consider tools like pg_stat_statements for query performance tracking, and monitor key metrics like connection count, cache hit ratio, and slow query logs.











