> ## Documentation Index > Fetch the complete documentation index at: https://bunny.net/docs/llms.txt > Use this file to discover all available pages before exploring further. # PostgreSQL > Deploy PostgreSQL to Magic Containers This guide walks you through deploying PostgreSQL to Magic Containers, either as a standalone container or as part of a [multi-container](https://bunny.net/docs/magic-containers/multi-container.md) app alongside your application. > [!TIP] > > Don't have a bunny.net account yet? [Sign up](https://dash.bunny.net/auth/register) and enable Magic Containers to get started. ## Quickstart **1. Create a new app** Go to the [bunny.net dashboard](https://dash.bunny.net), select **Magic Containers**, and click **Add App**. Select **Single region deployment**. > [!WARNING] > > Databases should use single region deployment with a single instance. Each pod gets its own dedicated volume with no data replication — this applies both across regions and within the same region. Scaling to multiple pods or regions would result in separate, isolated databases each with their own data. **2. Add the PostgreSQL container** Click **Add Container** and configure the image: * **Registry**: Docker Hub * **Image**: `library/postgres` * **Tag**: `17-alpine` Magic Containers will automatically detect the required endpoint and environment variables for the image. Configure the environment variables as needed: * `POSTGRES_USER` = `postgres` * `POSTGRES_PASSWORD` = a strong password * `POSTGRES_DB` = `app` * `PGDATA` = `/var/lib/postgresql/data/pgdata` **3. Add a persistent volume** In the **Volumes** section of the container settings, add a volume: * **Name**: `postgres-data` * **Mount path**: `/var/lib/postgresql/data` This ensures your database files persist across restarts and redeployments. Without a volume, all data is lost when the container stops. See [persistent volumes](https://bunny.net/docs/magic-containers/persistent-volumes.md) for more details on volume behavior and pricing. **4. Deploy** Review your settings and click **Confirm and Create**. > [!NOTE] > > Always set a strong `POSTGRES_PASSWORD`, even if the database is not exposed > externally. Other containers in the same pod can access the network, and a > password protects against accidental or unauthorized access. > [!WARNING] > > You must set `PGDATA` to a subdirectory of the volume mount (e.g. > `/var/lib/postgresql/data/pgdata`). PostgreSQL requires the data directory to > be empty on first initialization, and the volume mount point itself may > contain system files. ## Environment variables The official PostgreSQL image supports these environment variables: | Variable | Description | Default | | - | - | - | | `POSTGRES_USER` | Superuser name | `postgres` | | `POSTGRES_PASSWORD` | Superuser password (required) | - | | `POSTGRES_DB` | Default database created on first start | `postgres` | | `PGDATA` | Data directory inside the container | `/var/lib/postgresql/data` | ## Connect from your app In a [multi-container](https://bunny.net/docs/magic-containers/multi-container.md) setup, your app and PostgreSQL share the same localhost network. Connect using `127.0.0.1` and the default port `5432`. > [tab] Connection string > > ``` > postgresql://postgres:YOUR_PASSWORD@127.0.0.1:5432/app > ``` > [tab] Node.js > > ```javascript theme={null} > import pg from "pg"; > > const pool = new pg.Pool({ > connectionString: process.env.DATABASE_URL, > }); > > const result = await pool.query("SELECT NOW()"); > ``` > [tab] Go > > ```go theme={null} > import ( > "database/sql" > "os" > > _ "github.com/lib/pq" > ) > > db, err := sql.Open("postgres", os.Getenv("DATABASE_URL")) > if err != nil { > log.Fatal(err) > } > defer db.Close() > ``` > [tab] Python > > ```python theme={null} > import psycopg2 > import os > > conn = psycopg2.connect(os.environ["DATABASE_URL"]) > cur = conn.cursor() > cur.execute("SELECT NOW()") > ``` > [tab] PHP > > ```php theme={null} > > $dsn = "pgsql:host=127.0.0.1;port=5432;dbname=app"; > $pdo = new PDO($dsn, 'postgres', getenv('POSTGRES_PASSWORD')); > > $result = $pdo->query('SELECT NOW()')->fetch(); > ``` ## Multi-container example A typical setup pairs PostgreSQL with your application. When configuring the app, add two containers: ### App container * **Image**: your app image (e.g. `ghcr.io//my-app:latest`) * **Endpoint**: the port your app listens on * **Environment variables**: * `DATABASE_URL` = `postgresql://postgres:YOUR_PASSWORD@127.0.0.1:5432/app` ### PostgreSQL container * **Image**: `postgres:17-alpine` * **Volume**: mount path `/var/lib/postgresql/data` * **Environment variables**: * `POSTGRES_USER` = `postgres` * `POSTGRES_PASSWORD` = a strong password * `POSTGRES_DB` = `app` * `PGDATA` = `/var/lib/postgresql/data/pgdata` > [!NOTE] > > Both containers share the same localhost network, so your app connects to > PostgreSQL at `127.0.0.1:5432`. See [multi-container > apps](https://bunny.net/docs/magic-containers/multi-container.md) for more details. ## External access To connect to PostgreSQL from outside Magic Containers (e.g. from your local terminal), add an [Anycast endpoint](https://bunny.net/docs/magic-containers/endpoints.md): 1. Go to your app's **Endpoints** tab and click **Add New Endpoint** 2. Select **Anycast** as the type 3. Set **Container Port** to `5432` 4. Set **Exposed Port** to `5432` 5. Click **Add Endpoint** Then connect using the Anycast IP and the **exposed port**: ```bash theme={null} psql -h -p -U postgres -d app ``` > [!NOTE] > > The exposed port and container port may differ. When connecting externally, > always use the exposed port shown in your endpoint configuration. > [!WARNING] > > Exposing your database to the internet means anyone with the credentials can > connect. Use a strong password and consider removing the Anycast endpoint when > external access is no longer needed. ## TimescaleDB [TimescaleDB](https://www.timescale.com/) is a PostgreSQL extension for time-series data. It uses the same base configuration as PostgreSQL with a different image. ### Configuration Use the official TimescaleDB image instead of the standard PostgreSQL image: * **Registry**: Docker Hub * **Image**: `timescale/timescaledb` * **Tag**: `latest-pg17` (or another [supported tag](https://hub.docker.com/r/timescale/timescaledb/tags)) Configure the same environment variables as PostgreSQL: * `POSTGRES_USER` = `postgres` * `POSTGRES_PASSWORD` = a strong password * `POSTGRES_DB` = `app` * `PGDATA` = `/var/lib/postgresql/data/pgdata` > [!WARNING] > > The `PGDATA` environment variable is required when mounting a volume at > `/var/lib/postgresql/data`. The mount point may contain a `lost+found` > directory or other system files, which causes TimescaleDB initialization to > fail. Setting `PGDATA` to a subdirectory (e.g., `/var/lib/postgresql/data/pgdata`) > resolves this issue. ### Volume configuration Add a persistent volume with the same mount path as PostgreSQL: * **Name**: `timescaledb-data` * **Mount path**: `/var/lib/postgresql/data` ### Connection TimescaleDB uses the same connection methods as PostgreSQL. Connect using port `5432`: ``` postgresql://postgres:YOUR_PASSWORD@127.0.0.1:5432/app ``` After connecting, enable the TimescaleDB extension in your database: ```sql theme={null} CREATE EXTENSION IF NOT EXISTS timescaledb; ``` This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.