Managed PostgreSQL

A managed, relational database you can provision and link to your projects in seconds. PostgreSQL is one of the managed database services Temps runs as a container on your own infrastructure.


Creating a PostgreSQL Instance

Create a PostgreSQL database

  1. 1

    Navigate to Services then click Create Service.

  2. 2

    Select PostgreSQL.

  3. 3

    Choose the image: PostgreSQL 18 (Managed + WAL-G) for point-in-time recovery, or a Custom image like postgres:17-alpine for snapshot backups only.

    Checkpoint: Pick PostgreSQL 18 (Managed + WAL-G) if you need PITR -- custom images do not support it.

  4. 4

    Click Create Service. Temps pulls gotempsh/postgres-walg:18-bookworm, assigns a host port from 5432, creates a Docker volume, and auto-generates the postgres superuser password.

    Checkpoint: Confirm the new service appears in the Services list with a running status.

Via Dashboard:

  1. Navigate to ServicesCreate Service.
  2. Select PostgreSQL.
  3. Choose the image:
    • PostgreSQL 18 (Managed + WAL-G) — the recommended default. This image bundles WAL-G for point-in-time recovery (PITR).
    • Custom image — bring your own (e.g. postgres:17-alpine). Custom images support basic snapshot backups only — no PITR.
  4. Click Create Service.

On creation Temps:

  • Pulls the image gotempsh/postgres-walg:18-bookworm (the managed default).
  • Auto-assigns a host port starting from 5432 and creates a dedicated Docker volume for the data directory.
  • Auto-generates a postgres superuser password.

Prefer the command line or an AI agent? See Manage from the CLI below — you can create, link, and manage the database there, or just ask a coding agent to do it for you.


Manage from the CLI

Create and run the full lifecycle with the @temps-sdk/cli tool. There are two paths — hand it to a coding agent (recommended), or run the commands yourself.

Drive it through an AI agent

Most clients never type these commands by hand. A coding agent — Claude Code, Codex, or Opencode — runs the @temps-sdk/cli calls (shown below) against the Temps API for you. Install the CLI skill once so the agent knows every command and flag:

npx skills add gotempsh/temps --skill temps-cli

Then describe the goal in plain language — "create a Postgres 18 database and link it to my project" — and the agent issues the commands — or call the skill directly by typing /temps-cli. New to AI agents? See how to call a skill for the step-by-step. Browse every skill on the AI Skills page.

The underlying commands

Whether the agent runs them or you do, these are the @temps-sdk/cli calls. Every list/show command also accepts --json for scripting.

Create a PostgreSQL 18 database

# Log in to your Temps server (stores credentials in ~/.temps)
bunx @temps-sdk/cli login

# Create a PostgreSQL 18 instance. PostgreSQL requires the database,
# username, and password parameters — set them with repeatable -s
# key=value flags. The password is generated strong, inline.
# Omitting docker_image defaults to the managed WAL-G image (with PITR).
bunx @temps-sdk/cli services create \
  -t postgres \
  -n my-database \
  -s database=appdb \
  -s username=appuser \
  -s password="$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 28)" \
  -y

Inspect & connect

# List every service and its status (note the numeric ID)
bunx @temps-sdk/cli services list

# Show full details, including the connection string
bunx @temps-sdk/cli services show --id 10

Link to a project (injects POSTGRES_URL, etc.)

# Link the service to a project so its env vars are injected on deploy
bunx @temps-sdk/cli services link --id 10 --project-id 5

# See exactly which variables were injected
bunx @temps-sdk/cli services env --id 10 --project-id 5

# Unlink when you no longer need it
bunx @temps-sdk/cli services unlink --id 10 --project-id 5

Lifecycle

# Stop / start the container
bunx @temps-sdk/cli services stop --id 10
bunx @temps-sdk/cli services start --id 10

# Upgrade the image version
bunx @temps-sdk/cli services upgrade --id 10 -v postgres:18-alpine

# Permanently remove the service (destroys data — see the warning below)
bunx @temps-sdk/cli services remove --id 10 -f

Connecting to PostgreSQL

Node.js/TypeScript

import { Pool } from 'pg';

// POSTGRES_URL is injected automatically when the service is linked
const pool = new Pool({ connectionString: process.env.POSTGRES_URL });

const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);

Python

import os
import psycopg2

conn = psycopg2.connect(os.environ['POSTGRES_URL'])

cursor = conn.cursor()
cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,))

Linking to Projects

Link the database to a project

  1. 1

    Open the PostgreSQL service in the dashboard.

  2. 2

    Add a link to the target project and environment so Temps creates a per-tenant database named <project>_<environment> on the instance.

  3. 3

    Confirm the link. Temps injects POSTGRES_URL, POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB, POSTGRES_USER, and POSTGRES_PASSWORD into that environment.

    Checkpoint: Check the project environment variables to confirm POSTGRES_URL was injected.

When you link a PostgreSQL service to a project + environment, Temps creates a per-tenant database named <project>_<environment> on the instance and injects connection details into that environment:

VariableDescription
POSTGRES_URLFull connection string
POSTGRES_HOSTContainer hostname
POSTGRES_PORTPort (5432)
POSTGRES_DBPer-tenant database name (also exposed as POSTGRES_DATABASE / POSTGRES_NAME)
POSTGRES_USERUsername
POSTGRES_PASSWORDPassword

Access in your app

const pool = new Pool({ connectionString: process.env.POSTGRES_URL });

PostgreSQL WAL Health

Managed PostgreSQL services run a read-only WAL/archive health probe as part of the background health monitor (a ~30-second poll). The probe never writes to your database — it only reads PostgreSQL system views and inspects the pg_wal/ directory — and the result is stored on the service so the dashboard can surface problems before they fill your disk.

What the probe checks

The probe reads pg_ls_waldir(), pg_settings (max_wal_size, archive_mode, archive_command), replication-slot state, and the archiver status, then computes warnings. Each warning carries a severity of warning or critical:

ConditionTriggers whenSeverity
WAL bloatpg_wal size is at least 3× max_wal_sizecritical at 10×, otherwise warning
Stale replication slotAn inactive slot is retaining ≥ 3× max_wal_size of WALcritical
Archive backlogAt least 100 .ready segments in pg_wal/archive_statuswarning
WAL not recycledThe oldest WAL segment is at least 24h old while pg_wal is bloatedwarning

Reading the snapshot

The latest snapshot is exposed at:

GET /api/external-services/{id}/wal-health

It requires the ExternalServicesRead permission and returns a PostgresWalHealth snapshot (pg_wal size, max_wal_size, archive_mode, archive_command, archive backlog, archiver failure counts, stale slots, oldest-WAL age, and the computed warnings). The web console renders this as a warning panel on the service detail page.


Backups & Durability

Restore from a backup

  1. 1

    Open the PostgreSQL service in the dashboard and go to its Backups.

  2. 2

    Check what restore modes the service supports -- only the managed WAL-G image supports point-in-time recovery; custom images are snapshot-only.

    Checkpoint: Run bunx @temps-sdk/cli services restore-capabilities --id 10 to confirm PITR is available before restoring.

  3. 3

    Pick the backup to restore from, and for PITR set the target ISO 8601 timestamp.

  4. 4

    Start the restore (in-place or into a new service).

    Checkpoint: Watch the restore run status until it completes -- track it with bunx @temps-sdk/cli services restore-runs --id 10.

The managed gotempsh/postgres-walg image bundles WAL-G, which archives WAL to your configured S3 destination and enables point-in-time recovery (PITR) — PostgreSQL is the only managed service that supports PITR. Custom images fall back to basic snapshot backups with no PITR.

Backups run on a schedule you configure, and each schedule has its own retention window (in days) — Temps does not impose a fixed default retention. Restores are driven from the CLI:

Restore via the Temps CLI

# Inspect what restore modes a service supports (in-place / new service / PITR)
bunx @temps-sdk/cli services restore-capabilities --id <id>

# List backups stored on an S3 source
bunx @temps-sdk/cli services list-backups --s3-source-id <id>

# Start a restore (point-in-time for PostgreSQL, optionally cloned into a new service)
bunx @temps-sdk/cli services restore --id <id> --backup-id <id> [--pitr <iso>] [--new-service auto]

A confirmed write to PostgreSQL is durable — data is fsynced before the client receives acknowledgment. The built-in WAL health probe watches for WAL bloat and misconfigured archiving so a stalled archive destination can't silently fill your disk.


Monitoring

View live metrics and health in the dashboard's Services section — CPU & memory usage, connection count, disk usage, uptime, and slow-query detection. Service-level monitoring is not yet exposed through the CLI.


Resource Limits

You can cap how much memory, swap, and CPU the service's container is allowed to use, and inspect what it's actually consuming. A service with no limits set runs unconstrained (the default). The same controls apply to every managed service type — PostgreSQL (standalone and cluster), MongoDB, Redis, RustFS, and S3.

The service detail page shows a Resources card that polls runtime status every 30 seconds and live usage every 5 seconds, plus an Edit limits dialog.

Editing limits

Open Services → select the service → Resources card → Edit limits. The dialog has independent Memory and CPU toggles:

FieldMeaningEmpty / off
MemoryHard memory cap, in MiBUnlimited
SwapExtra swap above the memory cap, in MiBNo extra swap
CPUCPU cap, in coresUnlimited

Endpoints

All served under the /api prefix:

GET   /api/external-services/{id}/runtime
GET   /api/external-services/{id}/stats
PATCH /api/external-services/{id}/resources
  • GET …/runtime (ExternalServicesRead) — a per-container docker inspect snapshot: status, restart_count (a steady climb signals a crash loop), oom_killed, last exit_code, image, and the limits actually applied (so you can detect drift between configured and live caps).
  • GET …/stats (ExternalServicesRead) — a one-shot CPU/memory usage sample per container, cheap enough to poll every 5–10s. memory_percent is measured against the memory limit when one is set, or against host RAM otherwise.
  • PATCH …/resources (ExternalServicesWrite) — persists the caps to the service's encrypted config and live-applies them via Docker's update API (no restart needed). A request where every field is null removes all limits.

Security

  • Name
    Network Isolation
    Description
    • Reachable only from containers on the same Temps internal network — the port is never exposed to the public internet
    • Apps connect using the container name as the host, over private networking
    • Credentials are auto-generated at creation; you never have to choose a password
  • Name
    Credential Handling
    Description
    • Temps injects credentials as environment variables when the service is linked — never hard-code them
    • Credentials are stored encrypted at rest (see Encryption below)
    • The connection string and password are always retrievable via services show --id <id>, so you never need to store them yourself

Encryption

Credentials (passwords, access keys) are encrypted with AES-256-GCM before being written to the Temps database. The key lives at ~/.temps/encryption_key — protect this file and back it up separately from the database.

Database contents are not encrypted at the application layer by default. For encryption at rest, enable it at the infrastructure level — encrypted volumes or LUKS on bare metal; Temps does not manage disk encryption. In transit, traffic stays on the internal Docker network and is never exposed to the public internet (managed PostgreSQL starts with SSL disabled for this reason).


Deleting the Service

Delete the service

  1. 1

    Download a backup first -- open Services, select the service, go to Backups, and download the latest.

    Checkpoint: Confirm the backup downloaded successfully before destroying anything -- deletion cannot be undone.

  2. 2

    Unlink all projects so no running app still holds a reference to the credentials.

  3. 3

    Delete the service from its settings page and confirm.

    Checkpoint: Confirm the service no longer appears in the Services list.

  1. Download a backupServices → select the service → Backups → download the latest.
  2. Unlink all projects so no running app still holds a reference to the credentials.
  3. Delete the service from its settings page and confirm.

Pricing & Ownership

Because the service runs on your own infrastructure, there are no per-GB storage charges, connection caps, or metered bandwidth fees. A recommended starting allocation:

TierAllocation
Small1 GB RAM, 10 GB storage
Medium2 GB RAM, 50 GB storage
Large4 GB+ RAM, 200 GB+ storage

PostgreSQL is built in — Vercel bills Vercel Postgres separately, Netlify is external-only, and Railway includes it. Your data never leaves your server; Temps (the company) has no access to it. See Data Ownership & Privacy for the full policy, including GDPR.

Was this page helpful?