Deploy with a Database
This tutorial walks you through provisioning a managed PostgreSQL database on Temps, connecting it to your application, and deploying. No external database providers, no connection string juggling — Temps runs the database on your server and wires everything together automatically.
What you will build
By the end of this tutorial, you will have:
- A managed PostgreSQL database running as a Docker container on your server
- The database linked to your project with environment variables injected automatically
- An application deployed and connected to the database
- A Redis cache added as a bonus second service
- Knowledge of how Temps manages per-environment database isolation
Time required: approximately 15 minutes.
What this tutorial covers and does not cover:
| Covered | Not covered |
|---|---|
| Provisioning PostgreSQL and Redis | MongoDB and S3 storage (same process, different service type) |
| Linking services to projects | Database migrations and schema management |
| Automatic environment variable injection | Connection pooling and performance tuning |
| Per-environment database isolation | Replication and high availability |
| Querying data from the dashboard | Backup and restore (previous tutorial) |
Prerequisites
You need:
- A running Temps instance with a deployed project (complete Your First Deployment first)
- An application that uses a database — any framework that reads a database connection string from an environment variable will work. This tutorial shows examples for Next.js, Express, and generic Node.js.
If your current project does not use a database, that is fine. You can still follow along to provision the service and link it — then add database code later.
Create a PostgreSQL service
A managed service in Temps is a Docker container that Temps provisions, monitors, and backs up for you. You create the service once and then link it to any number of projects.
- In the sidebar, click Services
- Click Create Service
- Select PostgreSQL as the service type
- Configure it:
- Name
Name- Description
A unique name for this service, e.g.
my-app-db. This becomes part of the Docker container name (postgres-my-app-db) and the volume name.
- Name
Parameters- Description
Temps auto-fills sensible defaults. You can adjust:
- Port — The host port to bind. Auto-assigned starting from 5432. Bound to
127.0.0.1only (not publicly accessible). - Docker Image — Default:
gotempsh/postgres-walg:18-bookworm(PostgreSQL 18 with WAL-G for streaming backups). You can also usetimescale/timescaledb-ha:pg18if you need TimescaleDB. - Max Connections — Default:
100. Increase for high-concurrency applications.
Fields that are auto-generated and read-only after creation:
- Database —
postgres(the root database; per-project databases are created automatically when linked) - Username —
postgres - Password — A random 16-character alphanumeric string, encrypted at rest
- Port — The host port to bind. Auto-assigned starting from 5432. Bound to
Click Create. Temps pulls the Docker image (if not cached), creates a container and a persistent volume, starts the service, and waits for the health check (pg_isready) to pass.
When the status shows Running, your database is ready.
Where does data live? PostgreSQL data is stored in a Docker volume named postgres-{service-name}_data, mounted at /var/lib/postgresql inside the container. The volume persists across container restarts and Temps upgrades. Back it up with the backup system for off-site protection.
Link the service to your project
Creating a service does not automatically connect it to any project. You need to explicitly link them.
- Open your project in the dashboard
- Click Services in the project sidebar
- Click Link Service
- Select the PostgreSQL service you just created
- Click Link
That is it. The next time your project deploys, Temps will inject the database connection details as environment variables into your application container.
What linking does under the hood
When you link a PostgreSQL service to a project, Temps:
- Creates a dedicated database inside the PostgreSQL instance named
{project_slug}_{environment_slug}(e.g.my_app_production). Each project-environment combination gets its own isolated database. - Generates environment variables with the connection details, using the Docker container name as the hostname (so containers can talk to each other over the Docker network).
- Stores the link in a
project_servicestable — only one service of each type can be linked per project.
One service, many projects: A single PostgreSQL service can be linked to multiple projects. Each project gets its own database inside the same PostgreSQL instance. This is efficient for a single server — you run one PostgreSQL container instead of one per project.
Understand the injected environment variables
When a PostgreSQL service is linked to your project, Temps injects these environment variables at deploy time:
- Name
POSTGRES_URL- Description
The full connection string:
postgres://postgres:PASSWORD@postgres-SERVICE-NAME:5432/PROJECT_ENV_DBThis is the variable most frameworks expect. The hostname is the Docker container name (e.g.
postgres-my-app-db), the port is always5432(the internal port, not the host-mapped port), and the database name is your project-environment combination.
- Name
POSTGRES_HOST- Description
The container hostname:
postgres-my-app-db
- Name
POSTGRES_PORT- Description
The internal port:
5432
- Name
POSTGRES_NAME- Description
The root database name:
postgres
- Name
POSTGRES_DATABASE- Description
The per-project database name:
my_app_production
- Name
POSTGRES_USER- Description
The username:
postgres
- Name
POSTGRES_PASSWORD- Description
The auto-generated password.
Preview before deploying
To see exactly which variables will be injected without deploying:
- Go to your project's Services page
- Click on the linked PostgreSQL service
- Click Preview Environment to see all variable names and masked values
Common framework mappings
Most frameworks expect DATABASE_URL rather than POSTGRES_URL. You have two options:
Option A: Add an alias environment variable (recommended)
In your project's Environment Variables page, add:
| Key | Value |
|---|---|
DATABASE_URL | $POSTGRES_URL |
Temps supports variable interpolation — $POSTGRES_URL expands to the actual connection string at deploy time.
Option B: Update your application code
Read from POSTGRES_URL instead of DATABASE_URL:
// Before
const db = new Pool({ connectionString: process.env.DATABASE_URL });
// After
const db = new Pool({ connectionString: process.env.POSTGRES_URL });
Update your application
Add database access to your application. Below are examples for common frameworks. The key point: read the connection string from POSTGRES_URL (or DATABASE_URL if you set up the alias).
Connect to PostgreSQL
// db/index.ts
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.POSTGRES_URL,
});
export const db = drizzle(pool);
Add a health check that verifies database connectivity
A good health endpoint confirms the database is reachable:
api/health/route.ts (Next.js App Router)
import { Pool } from 'pg';
import { NextResponse } from 'next/server';
const pool = new Pool({
connectionString: process.env.POSTGRES_URL,
});
export async function GET() {
try {
await pool.query('SELECT 1');
return NextResponse.json({ status: 'ok', database: 'connected' });
} catch (error) {
return NextResponse.json(
{ status: 'error', database: 'disconnected' },
{ status: 500 }
);
}
}
This gives your uptime monitor a way to detect database outages, not just application crashes.
Deploy and verify
Commit your changes and push:
git add .
git commit -m "feat: add PostgreSQL database connection"
git push
Temps detects the push, builds your application, and deploys it. This time, the deploy includes the database environment variables.
Verify the connection
After the deployment completes:
- Visit your application's health endpoint (e.g.
https://my-app.yourdomain.com/healthor/api/health) - Confirm you see
"database": "connected"in the response - In the Temps dashboard, go to your project's Services page — the linked service should show the environment variables that were injected
Troubleshooting
If your application fails to connect to the database:
- Name
Connection refused- Description
Your app is trying to connect to
localhostinstead of the container name. Make sure you are readingPOSTGRES_URL(which uses the Docker container hostname) rather than hardcodinglocalhost.
- Name
Database does not exist- Description
Temps creates the per-project database on first deploy. If you linked the service after the last deploy, trigger a new deployment (push a commit or use the Redeploy button).
- Name
Authentication failed- Description
The password in
POSTGRES_URLis auto-generated. Do not set the password manually in your environment variables — let Temps inject it.
- Name
Timeout connecting- Description
Check that the PostgreSQL service is running. Go to Services in the sidebar and verify the status is Running. If it is stopped, click Start.
Explore your data
Temps includes a built-in data explorer that lets you query your databases directly from the dashboard without installing any tools.
- Open your project in the dashboard
- Click Services in the project sidebar
- Click on your linked PostgreSQL service
- Use the Query tab to run SQL
-- See all tables
SELECT tablename FROM pg_tables WHERE schemaname = 'public';
-- Check your data
SELECT * FROM users LIMIT 10;
-- Database size
SELECT pg_size_pretty(pg_database_size(current_database()));
This is useful for quick debugging and data inspection. For ongoing database management, connect with your preferred client (pgAdmin, DBeaver, TablePlus) using the host port shown on the service details page:
# Connect from your local machine (requires SSH tunnel or direct access)
psql "postgres://postgres:PASSWORD@your-server-ip:PORT/my_app_production"
Security: The PostgreSQL port is bound to 127.0.0.1 on the server, meaning it is not publicly accessible. To connect from your local machine, use an SSH tunnel: ssh -L 5432:localhost:PORT user@your-server-ip, then connect to localhost:5432.
Add Redis for caching
The process for adding any managed service is the same. Here is how to add Redis:
- Go to Services in the sidebar
- Click Create Service
- Select Redis
- Give it a name, e.g.
my-app-cache - Click Create — Temps starts a Redis container (
gotempsh/redis-walg:8-bookworm)
Link it to your project:
- Open your project's Services page
- Click Link Service, select the Redis service
- Click Link
On next deploy, these additional environment variables are injected:
- Name
REDIS_URL- Description
Full connection string:
redis://:PASSWORD@redis-my-app-cache:6379/DB_NUMBER
- Name
REDIS_HOST- Description
Container hostname:
redis-my-app-cache
- Name
REDIS_PORT- Description
Internal port:
6379
- Name
REDIS_PASSWORD- Description
Auto-generated password.
- Name
REDIS_DATABASE- Description
A database number (0-15) assigned per project, derived from a hash of the project and environment slugs.
Use Redis in your application:
lib/redis.ts
import { createClient } from 'redis';
export const redis = createClient({
url: process.env.REDIS_URL,
});
redis.connect();
Push and deploy. Your application now has both a database and a cache, managed entirely by Temps.
What you have accomplished
You now have a complete data stack running on your server:
| Service | Container | Purpose |
|---|---|---|
| PostgreSQL | postgres-my-app-db | Primary database with per-environment isolation |
| Redis | redis-my-app-cache | Caching and session storage |
Key concepts you learned:
- Services are shared: One PostgreSQL instance can serve multiple projects, each getting its own database
- Environment isolation: Temps creates
{project}_{environment}databases automatically, so production and staging data never mix - Automatic wiring: Link a service to a project and Temps handles the environment variables, networking, and database creation
- Docker networking: Containers communicate using Docker container names as hostnames over an internal network. The database port is only exposed to
127.0.0.1on the host.
Available service types
The same workflow applies to all managed services:
| Service | Default Image | Environment Variables |
|---|---|---|
| PostgreSQL | gotempsh/postgres-walg:18-bookworm | POSTGRES_URL, POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DATABASE, POSTGRES_USER, POSTGRES_PASSWORD |
| Redis | gotempsh/redis-walg:8-bookworm | REDIS_URL, REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_DATABASE |
| MongoDB | gotempsh/mongodb-walg:8.0 | MONGODB_URL, MONGODB_HOST, MONGODB_PORT, MONGODB_DATABASE, MONGODB_USERNAME, MONGODB_PASSWORD |
| S3 Storage | rustfs/rustfs:1.0.0-alpha.78 | S3_BUCKET, S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY |