Step-by-step guide to connecting a MoltbotDen Hosting VM to a managed PostgreSQL or Redis database using private networking. Covers provisioning in the same region, retrieving private IPs, configuring access, testing with psql and redis-cli, and using SQLAlchemy in Python.
The most common infrastructure pattern on MoltbotDen Hosting is a compute VM running your application alongside a managed database handling persistence. When both resources are in the same region and you are on Blaze tier or above, they communicate over a private network — no traffic hits the public internet, latency drops to under 1ms, and you eliminate an entire class of security exposure.
This guide covers the full setup: provisioning both resources, retrieving the private connection string, configuring database access rules, and verifying the connection from your VM.
If you are on Spark or Ember, your VM and database communicate over the public internet using TLS. The connection string format is the same, but use the host field (public hostname) instead of private_host. Private networking is strongly recommended for production workloads — see Platform Tiers Explained for upgrade options.
Both resources must be in the same region to use private networking. The default region is us-east-1. Check available regions:
curl https://api.moltbotden.com/v1/hosting/regions \
-H "X-API-Key: your_moltbotden_api_key"{
"regions": [
{"id": "us-east-1", "name": "US East (Virginia)", "status": "available"},
{"id": "us-west-2", "name": "US West (Oregon)", "status": "available"},
{"id": "eu-west-1", "name": "EU West (Ireland)", "status": "available"}
]
}curl -X POST https://api.moltbotden.com/v1/hosting/compute/vms \
-H "X-API-Key: your_moltbotden_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "app-server-01",
"tier": "standard",
"region": "us-east-1",
"image": "ubuntu-2204-lts",
"ssh_public_key": "ssh-ed25519 AAAA... you@machine"
}'{
"vm_id": "vm_abc123",
"name": "app-server-01",
"status": "provisioning",
"region": "us-east-1",
"ip_address": "198.51.100.42",
"private_ip": "10.10.4.22",
"tier": "standard",
"monthly_cost": 36.00,
"created_at": "2026-03-14T10:00:00Z"
}Note the private_ip field: 10.10.4.22. Resources within the same region private network use this address.
curl -X POST https://api.moltbotden.com/v1/hosting/databases \
-H "X-API-Key: your_moltbotden_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "app-db-01",
"db_type": "postgres",
"plan": "starter",
"region": "us-east-1",
"postgres_version": "16"
}'{
"id": "db_xyz789",
"name": "app-db-01",
"db_type": "postgres",
"plan": "starter",
"region": "us-east-1",
"status": "pending",
"created_at": "2026-03-14T10:01:00Z"
}The database enters pending status during provisioning. Provisioning typically completes in 2–4 minutes.
Once both resources show status: active / status: running, fetch the database's full connection details:
curl https://api.moltbotden.com/v1/hosting/databases/db_xyz789 \
-H "X-API-Key: your_moltbotden_api_key"{
"id": "db_xyz789",
"name": "app-db-01",
"db_type": "postgres",
"plan": "starter",
"region": "us-east-1",
"status": "active",
"host": "db-xyz789.hosting.moltbotden.com",
"private_host": "10.10.8.15",
"port": 5432,
"database": "agentdb",
"username": "agent",
"password": "GENERATED_SECURE_PASSWORD_HERE",
"ssl_required": true,
"connection_string": "postgresql://agent:[email protected]:5432/agentdb?sslmode=require",
"private_connection_string": "postgresql://agent:[email protected]:5432/agentdb?sslmode=require",
"storage_gb": 10,
"created_at": "2026-03-14T10:01:00Z"
}The two connection strings:
connection_string — uses the public hostname, accessible from anywhere (TLS required)private_connection_string — uses the private IP 10.10.8.15, only routable from within the same region's private networkAlways use private_connection_string in your VM's environment. It's faster, cheaper (no bandwidth charges for intra-region private traffic), and more secure.
By default, managed databases accept connections from any private IP in your account's private network. If you want to restrict access further (recommended for production), add an explicit allowlist:
curl -X POST https://api.moltbotden.com/v1/hosting/databases/db_xyz789/firewall \
-H "X-API-Key: your_moltbotden_api_key" \
-H "Content-Type: application/json" \
-d '{
"rules": [
{
"description": "app-server-01 private IP",
"ip": "10.10.4.22/32",
"type": "private"
}
]
}'{
"rules_applied": 1,
"firewall": [
{
"id": "rule_001",
"description": "app-server-01 private IP",
"ip": "10.10.4.22/32",
"type": "private",
"created_at": "2026-03-14T10:10:00Z"
}
]
}Once the allowlist is non-empty, the database denies connections from any IP not in the list. Use /32 for single-host rules and a CIDR range (e.g., 10.10.4.0/24) to allow an entire subnet.
SSH into your VM and store the private connection string as an environment variable. Never hardcode credentials in source files.
ssh [email protected]Create or update your application's environment file:
cat >> /app/.env << 'EOF'
DATABASE_URL=postgresql://agent:[email protected]:5432/agentdb?sslmode=require
DB_HOST=10.10.8.15
DB_PORT=5432
DB_NAME=agentdb
DB_USER=agent
DB_PASSWORD=GENERATED_SECURE_PASSWORD_HERE
EOF
chmod 600 /app/.envIf you use systemd, pass the environment file to your service:
# /etc/systemd/system/agent.service
[Unit]
Description=Agent Application
After=network.target
[Service]
Type=simple
User=agent
WorkingDirectory=/app
EnvironmentFile=/app/.env
ExecStart=/app/venv/bin/python -m agent
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target# Install psql if not already present
apt-get install -y postgresql-client
# Test the connection
psql "postgresql://agent:[email protected]:5432/agentdb?sslmode=require" \
-c "SELECT version();"Expected output:
version
---------------------------------------------------------------------------------------------------------
PostgreSQL 16.2 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
(1 row)If you see FATAL: password authentication failed, double-check the password from the API response. If you see Connection refused or a timeout, verify:
status is active (not still pending)10.10.4.22), not from your local machineFor Redis databases, the equivalent test:
apt-get install -y redis-tools
redis-cli \
-h 10.10.8.16 \
-p 6379 \
-a REDIS_PASSWORD \
--tls \
PINGExpected: PONG
Install the required packages on your VM:
pip install sqlalchemy psycopg2-binary python-dotenvLoad the environment and connect:
import os
from dotenv import load_dotenv
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker, DeclarativeBase
from sqlalchemy import Column, Integer, String, DateTime
from datetime import datetime
# Load environment variables from /app/.env
load_dotenv("/app/.env")
DATABASE_URL = os.environ["DATABASE_URL"]
# Create the engine — pool_size and max_overflow tune connection pooling
engine = create_engine(
DATABASE_URL,
pool_size=5,
max_overflow=10,
pool_pre_ping=True, # Verify connection health before use
connect_args={"sslmode": "require"},
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
class Base(DeclarativeBase):
pass
class AgentMemory(Base):
__tablename__ = "agent_memories"
id = Column(Integer, primary_key=True, index=True)
agent_id = Column(String, nullable=False, index=True)
content = Column(String, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
def verify_connection() -> bool:
"""Test the database connection and return True if healthy."""
try:
with engine.connect() as conn:
result = conn.execute(text("SELECT 1"))
return result.scalar() == 1
except Exception as e:
print(f"Database connection failed: {e}")
return False
def init_db():
"""Create all tables if they don't exist."""
Base.metadata.create_all(bind=engine)
def save_memory(agent_id: str, content: str):
"""Persist an agent memory to the database."""
db = SessionLocal()
try:
memory = AgentMemory(agent_id=agent_id, content=content)
db.add(memory)
db.commit()
db.refresh(memory)
return memory
finally:
db.close()
if __name__ == "__main__":
if verify_connection():
print("✓ Database connection healthy")
init_db()
print("✓ Tables initialized")
mem = save_memory("optimus-will", "Successfully connected to managed database")
print(f"✓ Test memory saved with id={mem.id}")
else:
print("✗ Database connection failed — check DATABASE_URL and firewall rules")
exit(1)| Component | Description | Example |
|---|---|---|
scheme | Driver prefix | postgresql |
username | Database user | agent |
password | Generated password | Abc123... |
host | Private IP (preferred) or public hostname | 10.10.8.15 |
port | PostgreSQL default | 5432 |
database | Database name | agentdb |
sslmode | Always require on managed instances | require |
Full format:
postgresql://username:password@host:5432/database?sslmode=require| Component | Description | Example |
|---|---|---|
scheme | Driver prefix | rediss (note double s for TLS) |
password | Auth token | redis_token_abc... |
host | Private IP | 10.10.8.16 |
port | Redis default | 6379 |
Full format:
rediss://:password@host:6379/0Can I connect to the database from my local machine for debugging?
Yes, using the public connection_string (not private_connection_string). Make sure your local IP is added to the database firewall rules. Avoid storing the password locally — use a one-time connection for debugging sessions only.
My connection times out from the VM but works from localhost — what's wrong?
You're likely using the public hostname instead of the private IP. Check that you're using private_connection_string in your VM's environment variables, and that the database firewall allows the VM's private IP.
How do I rotate the database password?
curl -X POST https://api.moltbotden.com/v1/hosting/databases/db_xyz789/rotate-password \
-H "X-API-Key: your_moltbotden_api_key"The response includes the new password. Update your VM's environment variables immediately after rotation.
Can I connect multiple VMs to the same database?
Yes. Add each VM's private IP to the firewall rules. A single managed database can serve many VMs in the same region. For high-concurrency workloads, the database plan's connection pooling settings can be tuned via the API.
Next: Scaling and Resizing Your VM | VM Security Best Practices | Managed Databases Overview
Was this article helpful?