Running PostgreSQL in a Docker container is not hard. What gets complicated is everything around it: setting up persistent volumes, managing credentials, making sure backups actually happen, exposing the connection securely to the services that consume the database, and doing all of this on infrastructure that responds quickly to users in Brazil. Each of these tasks alone takes about 20 minutes. Together, they eat up an entire afternoon.
Guara Cloud handles the surrounding work through the Service Catalog. You click on PostgreSQL, pick a resource tier, and the platform provisions the container, attaches a volume, generates a username and password, injects the connection string as an environment variable into other services in the same project, and sets up health checks. In two minutes you have a Postgres 17 instance running in Sao Paulo.
Quick answer
To host PostgreSQL in Brazil, use the Guara Cloud Service Catalog. The service deploys a PostgreSQL 17 container with a managed persistent volume, automatically generated credentials, and service discovery via environment variables. The connection string is available to other services in the project without manual configuration. Backups are optional and use managed storage snapshots.
Key takeaways
- PostgreSQL runs in an isolated Docker container inside your project, not a shared service.
- Credentials (username, password, database name) are generated automatically and visible in the dashboard.
- The platform injects
DATABASE_URLandDATABASE_HOSTas environment variables into other services in the same project. - A managed persistent volume keeps data across container restarts.
- The platform health check monitors database readiness. If the process dies, Kubernetes restarts the pod automatically.
- Billing is in Brazilian Real (BRL) through Stripe, no dollar conversion on your card statement.
When this applies
This workflow works for any project that needs a relational PostgreSQL database: REST APIs built with Node.js or NestJS, Python apps with Django or FastAPI, Go or Java microservices using JPA. If the application connects to PostgreSQL over TCP, this guide covers the infrastructure side.
It also applies when you need a database for development and staging that matches the production version. The catalog ships PostgreSQL 17, which is the latest stable release with support for JSONB, MERGE, and improved logical replication.
When to skip this
If the project requires extensions not bundled in the official Postgres Alpine image (PostGIS for geospatial data or pgvector for embeddings, for example), check whether the extension is available in the catalog image. If not, you can run Postgres as a custom service with your own Dockerfile instead.
For OLAP workloads with heavy analytical queries over terabytes of data, PostgreSQL is the wrong tool. Look at ClickHouse or DuckDB. The Guara Cloud catalog does not cover those yet.
Before you start
- A Guara Cloud account on the Pro plan or higher (the Service Catalog is not available on the Hobby plan)
- A project created in the dashboard
- The application that will use the database (can be a Node.js, Python, Go deploy, etc.)
1. Provision PostgreSQL from the catalog
In the Guara Cloud dashboard, open your project and click “New Service”. Select the “Service Catalog” tab and choose “PostgreSQL”. The platform shows the default version (17 Alpine) and resource options (CPU and memory).
Step by step in the dashboard
- In the project dashboard, click "New Service"
- Select the "Service Catalog" tab
- Choose PostgreSQL in the Databases section
- Select a resource tier (start with 512MB RAM and 0.5 vCPU for most apps)
- Click Deploy
Provisioning takes between 30 seconds and 2 minutes. The container comes up, the managed volume is attached, initdb runs, and the health check passes. When the status changes to “Running”, the database is ready.
2. Get your credentials
Once the service is running, click on it in the dashboard. The “Details” tab shows:
Auto-generated credentials
| Variable | Value (example) |
|---|---|
POSTGRES_USER | postgres |
POSTGRES_PASSWORD | auto-generated |
POSTGRES_DB | postgres |
DATABASE_URL | postgres://postgres:***@postgresql:5432/postgres |
These credentials are available in the dashboard and injected as environment variables into other services in the same project. If your Node.js or NestJS deploy is in the same project, it already sees DATABASE_URL without any extra configuration.
3. Connect your application
In your API code, read DATABASE_URL from the environment. Example with Prisma:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
} Or with TypeORM (NestJS):
TypeOrmModule.forRoot({
type: 'postgres',
url: process.env.DATABASE_URL,
autoLoadEntities: true,
synchronize: false, // never true in production
}) For Python apps with SQLAlchemy:
import os
from sqlalchemy import create_engine
engine = create_engine(
os.environ["DATABASE_URL"],
pool_size=10,
pool_pre_ping=True,
) The pool_pre_ping=True setting matters in containerized environments. If the Postgres pod restarted, old connections in the pool are dead. Pre-ping detects this and opens a fresh connection before executing the query.
4. Configure extra parameters if needed
Some applications need specific Postgres settings. You can pass parameters through the dashboard:
Common tuning parameters
| Parameter | Suggested value |
|---|---|
POSTGRES_MAX_CONNECTIONS | 100 |
POSTGRES_SHARED_BUFFERS | 128MB |
POSTGRES_WORK_MEM | 4MB |
For most small and mid-size web applications, Postgres defaults work fine. Only adjust when EXPLAIN ANALYZE tells you to.
5. Validate the connection
Before sending real traffic, connect to the database manually to confirm everything works. Use psql from one of the services in the project (which already has internal network access):
psql "$DATABASE_URL" -c "SELECT version();"
# Expected output:
# PostgreSQL 17.x on aarch64-unknown-linux-musl, compiled by gcc (Alpine) If psql is not installed in the application container, test from the dashboard using the web terminal (available in the PostgreSQL service tab).
Backups and persistence
The PostgreSQL service uses managed persistent storage, which keeps data across restarts. If the pod dies and Kubernetes schedules a new one, the volume is reattached automatically.
For backups, the catalog offers managed storage snapshots. Configure the snapshot policy in the service dashboard: frequency (every 6h, 12h, or 24h) and retention (how many snapshots to keep). A snapshot captures the entire volume, so a restore brings the database back to its exact state at that point in time.
If the project needs external backups (off-site, for compliance or disaster recovery), run pg_dump via a cron job and push the output to the MinIO service in the catalog (which is S3-compatible).
pg_dump "$DATABASE_URL" --format=custom --compress=9 > /tmp/backup.dump
# Upload to MinIO/S3 in the next step External connection (outside the project)
By default, the catalog PostgreSQL is only reachable by services in the same project over the internal Kubernetes network. If you need to connect from outside (a script on your local machine, a BI tool, a Metabase instance on a client server), use port forwarding through the dashboard or the external URL that Guara Cloud can generate for the service.
In practice, keeping the database accessible only internally is more secure. Admin tools like TablePlus or DBeaver connect through SSH tunnel or port-forward without exposing port 5432 to the internet.
Common issues
- Problem Application cannot connect: "connection refused"
- Solution Check that both services (database and application) are in the same project and that PostgreSQL shows "Running" status. If the status is "CrashLoopBackOff", check whether the volume is full in the resources tab.
- Problem Connection timeout (hangs then fails)
- Solution The database health check may not have passed yet. Wait for the status to show "Running" before trying to connect. If it is already Running, check the logs for crash recovery (takes longer on the first boot after a forced restart).
- Problem FATAL: password authentication failed
- Solution The application is using hardcoded credentials instead of reading DATABASE_URL from the environment. Confirm that the code reads process.env.DATABASE_URL (or equivalent) and that the variable is visible in the deployment.
- Problem Data disappeared after a redeploy
- Solution The managed volume persists data across restarts. If data is missing, check whether the volume was deleted manually or if the service was removed and recreated (which creates a new volume).
- Problem Queries that were fast locally are now slow
- Solution Run EXPLAIN ANALYZE on the slow queries. Check whether the indexes that exist locally were migrated to the cloud database (migrations do not run automatically). Confirm that shared_buffers and work_mem match the data size.
Migrating from an existing database
If you already have a PostgreSQL instance running elsewhere (AWS RDS, Railway, a self-hosted server) and want to move to Guara Cloud, the path is:
- Run
pg_dumpon the source database. - Provision PostgreSQL in the Guara Cloud catalog.
- Use
pg_restorepointing at the new database’sDATABASE_URL. - Validate row counts and run a few spot-check selects.
- Point the application at the new
DATABASE_URL.
# On the source server
pg_dump -h origin.example.com -U user -d mydb -Fc -f backup.dump
# Inside the Guara Cloud app container (or via port-forward)
pg_restore -d "$DATABASE_URL" --no-owner --no-privileges backup.dump For large databases (over 5GB), use pg_dump --format=directory --jobs=4 to parallelize the dump and pg_restore --jobs=4 to parallelize the restore. The time difference is significant.
Scaling the database
When the application grows, the initial resource tier may not keep up. In the Guara Cloud dashboard, you can scale CPU and memory for the PostgreSQL service without downtime (the pod does a rolling restart). Data lives on the persistent volume, so the container swap does not affect content.
Signs that it is time to scale: CPU consistently above 80%, queries doing sequential scans on large tables, connections hitting max_connections. Before scaling hardware, check whether a missing index or a rewritten query would solve the problem.
What PostgreSQL version does the catalog offer?
PostgreSQL 17 Alpine. This is the latest stable release with support for MERGE, better JSONB performance, and improved logical replication.
Is the PostgreSQL instance shared between projects?
No. Each instance is a dedicated container in your project with an exclusive volume. Your data never lives alongside another customer.
Can I use extensions like uuid-ossp or pgcrypto?
Yes. The official PostgreSQL Alpine image includes contrib extensions (uuid-ossp, pgcrypto, pg_trgm, and others). Just run CREATE EXTENSION after connecting.
How much does hosting PostgreSQL on Guara Cloud cost?
Cost depends on the resource tier you choose (CPU and memory). Billing is in Brazilian Real (BRL) through Stripe, prorated by usage time. There is no setup fee or data transfer cost between services in the same project.
Do I need to set up SSL for the connection between my app and the database?
No. Traffic between services in the same project flows over the internal Kubernetes network, which is isolated by namespace. SSL on the TCP connection would be redundant in this scenario.
How do I set up automated backups?
Configure managed storage snapshots in the PostgreSQL service dashboard. Choose a frequency (6h, 12h, or 24h) and retention policy. Snapshots are available for restore directly in the dashboard.
Deploy PostgreSQL on Guara Cloud
Dedicated container, auto-generated credentials, persistent volume, and snapshot backups. All in Sao Paulo, billing in BRL.