# Connect Your Laravel, Node.js or PHP App to PostgreSQL in xCloud

> See the database variables xCloud writes for a PostgreSQL site and connect Laravel, Node.js (pg or Prisma) or plain PHP (PDO) to managed PostgreSQL.

When you create a **PHP App**, **Laravel** or **Node.js** site with **Database engine** set to **PostgreSQL**, xCloud creates a database and a role for the site and writes the connection details to the site's `.env` file. Your app only has to read those variables. This guide lists the variables, shows how Laravel, Node.js (`pg` and Prisma) and plain PHP (PDO) read them, and covers the errors people hit most.

If PostgreSQL is not on your server yet, first follow [Install Managed PostgreSQL and pgAdmin in xCloud](/docs/install-managed-postgresql-and-pgadmin-in-xcloud/). That guide also shows how to pick PostgreSQL when you create a site.

## Before you start

- **Managed PostgreSQL shows Ready** on an Nginx server. It runs PostgreSQL 17 in Docker (Docker Engine 28 or newer) and is not available on OpenLiteSpeed servers.
- **A Git site that uses PostgreSQL.** The site's **App Type** is **PHP App**, **Laravel** or **Node.js**, and its **Database engine** was set to **PostgreSQL** when you created it.

## The variables xCloud writes

xCloud adds these seven lines to the site's `.env` file during the first deploy. It writes them last, so they replace any database values that came from your repository's `.env.example`.

```bash
DB_CONNECTION='pgsql'
DB_HOST='127.0.0.1'
DB_PORT='<port>'
DB_DATABASE='xc_site_<site id>'
DB_USERNAME='xc_site_<site id>'
DB_PASSWORD='<generated password>'
DATABASE_URL='postgresql://xc_site_<site id>:<generated password>@127.0.0.1:<port>/xc_site_<site id>'
```

| Variable | What it holds |
|---|---|
| `DB_CONNECTION` | Always `pgsql`, the name Laravel uses for its PostgreSQL driver |
| `DB_HOST` | Always `127.0.0.1`. PostgreSQL only listens on the server itself and is not reachable from the internet |
| `DB_PORT` | The port xCloud chose for the server's PostgreSQL. It is usually **not** 5432 |
| `DB_DATABASE` and `DB_USERNAME` | The site's own database and role. Both are named `xc_site_` plus the site's ID |
| `DB_PASSWORD` | A generated 40-character password made of letters and numbers |
| `DATABASE_URL` | The same details as one connection string, for Node.js libraries and Prisma |

The site's role owns its database. It cannot create other databases or roles. Use pgAdmin for that (see [Add more databases and roles in pgAdmin](#add-more-databases-and-roles-in-pgadmin)).

## Step 1: Find the variables in your site's .env file

**Laravel and Node.js sites:** open the site and go to **'Laravel' → 'Environment'** (or **'Node.js' → 'Environment'**). The editor shows the `.env` file with the `DB_` lines and `DATABASE_URL` at the end.

![The PostgreSQL DB_ variables and DATABASE_URL in 'Laravel' → 'Environment' for a site](/_landing/docs/connect-your-app-to-postgresql-in-xcloud-image-1.png)

You can edit values here and click **Update Environment**. For a Node.js site, saving restarts the app so it picks up the change. Leave the database lines as xCloud wrote them unless you deliberately point the app at another database.

**PHP App sites:** the dashboard has no Environment editor for PHP App sites. The file is `/var/www/<your site>/.env`, or `.env` in the folder you set as **.env File Path** in the site's **Git** settings. Open it over SSH or in the [web terminal](/docs/access-web-terminal-in-xcloud/).

## Step 2: Connect your app

### Laravel

Laravel reads `DB_CONNECTION`, `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME` and `DB_PASSWORD` with its default `config/database.php`, so nothing needs to change in code. After the deploy, run the migrations from your deploy script or on the server:

```bash
php artisan migrate --force
```

**Expected result:** the migrations run without a connection error. If you cached the config before the variables were written, run `php artisan config:clear` first.

### Node.js with pg

xCloud loads the site's `.env` file into the app's environment every time the app starts, so `process.env.DATABASE_URL` is set without the `dotenv` package.

```js
const { Pool } = require('pg');

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

const { rows } = await pool.query('SELECT version()');
console.log(rows[0].version);
```

You can also pass the separate values: `host: process.env.DB_HOST`, `port: Number(process.env.DB_PORT)`, `database: process.env.DB_DATABASE`, `user: process.env.DB_USERNAME`, `password: process.env.DB_PASSWORD`.

### Node.js with Prisma

Point the datasource at `DATABASE_URL` in `prisma/schema.prisma`:

```prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
```

Then apply your migrations in the site's deploy script, for example `npx prisma migrate deploy`. The Prisma CLI reads `DATABASE_URL` from the `.env` file in the project folder.

### Plain PHP with PDO

PHP on xCloud Nginx servers includes the `pdo_pgsql` driver. Plain PHP does not read `.env` files by itself, so load it first, for example with the `vlucas/phpdotenv` package (`composer require vlucas/phpdotenv`):

```php
<?php
require __DIR__ . '/vendor/autoload.php';

Dotenv\Dotenv::createImmutable(__DIR__)->load();

$dsn = sprintf('pgsql:host=%s;port=%s;dbname=%s',
    $_ENV['DB_HOST'], $_ENV['DB_PORT'], $_ENV['DB_DATABASE']);

$pdo = new PDO($dsn, $_ENV['DB_USERNAME'], $_ENV['DB_PASSWORD'], [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

echo $pdo->query('SELECT version()')->fetchColumn();
```

If your `.env` file is in a subfolder, pass that folder to `createImmutable()` instead of `__DIR__`.

## Step 3: Check the connection

Load a page or route that queries the database, or run the check from the server. **Expected result:** the query returns the PostgreSQL 17 version string. If it fails, find the error in the table below.

## Common errors

| Error | Cause | Fix |
|---|---|---|
| `connection refused` on port 5432 | The app uses 5432 instead of the port xCloud chose | Use `DB_PORT` or `DATABASE_URL` from the `.env` file, never a hard-coded port |
| `could not find driver` in Laravel or PDO | The app still uses the MySQL driver, or `DB_CONNECTION` is `mysql` | Set `DB_CONNECTION=pgsql` and use a `pgsql:` DSN, not `mysql:` |
| `Access denied` or `SQLSTATE[HY000] [2002]` | A MySQL error: the app is still connecting to MySQL | Check that the config reads the `.env` values above and clear any cached config |
| `password authentication failed` | The password or user was changed by hand, or the app logs in as `postgres` | Use `DB_USERNAME` and `DB_PASSWORD` exactly as written. The `postgres` admin user cannot log in over the network |
| `permission denied to create database` | The site role cannot create databases or roles | Create extra databases and roles in pgAdmin |
| Variables are empty in a PHP App | Plain PHP does not load `.env` files | Load the file with phpdotenv as shown above |
| Timeout when connecting from your computer | PostgreSQL only listens on 127.0.0.1 on the server | Work in pgAdmin, or run commands on the server over SSH |

## Add more databases and roles in pgAdmin

Each site gets one database and one role. To add more, for example a separate database for a queue or a read-only role for reports, use pgAdmin:

1. Go to the server's **'Management' → 'Database'** and click **Open pgAdmin** on the **pgAdmin** card. Sign in with the details from **Show credentials**.
2. In pgAdmin, right-click **Login/Group Roles** and choose **Create → Login/Group Role**. Give it a name, set a password on the **Definition** tab and allow **Can login?** on the **Privileges** tab.
3. Right-click **Databases** and choose **Create → Database**. Set **Owner** to the role you created.
4. Add the new values to your site's `.env` under your own variable names. Use `127.0.0.1` and the same `DB_PORT` as the site's other values.

pgAdmin shows the server on port 5432 because it connects from inside the private Docker network. Your app on the server still uses `DB_PORT`.

## Limits to know

- **Backups:** xCloud site backups do not include PostgreSQL databases yet. Export them from pgAdmin or with `pg_dump`.
- **Clone, migrate, staging and snapshots:** not available for PostgreSQL sites yet.
- **Deleting a site:** xCloud keeps the site's database and role. Remove them in pgAdmin if you no longer need them.
- **One-click PostgreSQL app:** the separate [PostgreSQL one-click app](/postgresql-hosting/) is a different setup. A server that runs it cannot add managed PostgreSQL.

For MySQL sites, see [Create a database for your site](/docs/create-a-database-for-your-site-web-application/). To deploy the app itself, see [Deploy a Node.js application](/docs/how-to-deploy-a-node-js-application/) or [Deploy Laravel applications](/docs/deploy-laravel-applications/).

## Frequently asked questions

### Why does my app fail to connect on port 5432?

Managed PostgreSQL does not listen on 5432 on the server. xCloud picks a port for it and writes that port to DB_PORT and DATABASE_URL in your site's .env file. Read the port from there instead of hard-coding 5432. pgAdmin shows 5432 because it connects from inside the private Docker network.

### Can I connect to managed PostgreSQL from my computer?

Not directly. Managed PostgreSQL only listens on 127.0.0.1 on the server, so it is not reachable from the internet. Use pgAdmin to work with your data, or run commands on the server over SSH.

### Can I log in to PostgreSQL as the postgres user?

No. The postgres admin user cannot log in over a network connection. Use the site's own role from DB_USERNAME, or create more databases and roles in pgAdmin.

### Does xCloud back up my PostgreSQL database?

Not yet. xCloud site backups do not include PostgreSQL databases. Export your database from pgAdmin or with pg_dump on a schedule you control.

If you run into any issues connecting your app to PostgreSQL, feel free to reach out to our [support team](/docs/access-built-in-support-portal-in-xcloud/).
