# SSH access and direct Postgres

> On Business and Enterprise, SSH into your SupaHost instance as a restricted user to run psql, read service logs and open a tunnel for direct Postgres connections from pg_dump, ORMs and GUI tools.

Source: https://supahost.app/docs/platform/ssh · Updated 2026-11-11

On the Business and Enterprise plans you can SSH into your instance as a restricted user. That gives you two things: a few admin commands (psql, service logs, status), and an SSH tunnel to Postgres so tools like `psql`, `pg_dump`, Prisma, DBeaver or TablePlus can connect directly.

Postgres on SupaHost listens only on the VM's localhost and isn't reachable from the internet on any plan. On Builder and Pro, use the Data API, Studio's SQL editor, the MCP `execute_sql` tool, or the exports on the Backups tab instead. See [Connecting to your database](/docs/database/connecting).

## How access is locked down

- You sign in as the user `supahost`. It has no password, no root access and no Docker access.
- Port 22 is open on the instance only while your plan includes SSH **and** at least one key is added. With no keys, the port is closed.
- With sudo, the account can run exactly four SupaHost commands and nothing else.
- Port forwarding is allowed only to Postgres, at `127.0.0.1:5432`.
- The account can't reach the cloud metadata server, so it can't pick up the VM's cloud credentials.

## Add your SSH key

1. Open **Dashboard → your project → Connect → SSH & CLI**.
2. Paste your public key into **Public key** and click **Add key**. Only the owner can add keys.
3. Wait up to a minute for the key to become active.

Supported key types: ed25519, RSA (2048 bits or more), ECDSA (256, 384 or 521), and hardware-backed sk-ed25519 and sk-ecdsa. You can add up to 20 keys per instance.

If you don't have a key yet:

```bash
ssh-keygen -t ed25519 -C "you@example.com"
cat ~/.ssh/id_ed25519.pub   # paste this line into the dashboard
```

## Connect

```bash
ssh supahost@acme.supahost.space
```

## Commands

Each command takes a project name. Run them with `sudo`.

| Command | What it does |
|---|---|
| `sudo supahost-psql <project> [-c "<sql>"]` | Opens psql on the project's database as the `postgres` role, or runs one statement. Reads SQL from stdin too |
| `sudo supahost-logs <project> [auth\|rest\|storage\|functions\|http] [--follow]` | Shows the last 200 lines of a service's logs; `--follow` keeps streaming |
| `sudo supahost-status` | Lists projects and containers, with disk, memory and uptime |
| `sudo supahost-db-url <project>` | Prints the project's database connection string |

Some examples:

```bash
# A quick count, from your laptop
ssh supahost@acme.supahost.space \
  sudo supahost-psql app-one -c "select count(*) from public.bookings"

# Run a migration file through stdin
ssh supahost@acme.supahost.space sudo supahost-psql app-one < 20261111_add_rooms.sql

# Follow Auth logs while you test a sign-in
ssh supahost@acme.supahost.space sudo supahost-logs app-one auth --follow

# Check every project and container on the instance
ssh supahost@acme.supahost.space sudo supahost-status
```

`http` logs are the gateway's access logs for the project, which are useful when you want to see each request's path and status code.

## Direct Postgres through a tunnel

Open a tunnel that forwards a port on your machine to Postgres on the instance:

```bash
ssh -N -L 5432:127.0.0.1:5432 supahost@acme.supahost.space
```

Leave that running. In the dashboard, **Connect → Database → Show connection string** (owner only) gives you a URL like this:

```
postgresql://postgres:<password>@127.0.0.1:5432/p_app_one
```

Each project is its own database, named `p_` plus the project name with dashes turned into underscores. The `postgres` role and its password are shared by the whole instance, so the same credentials reach every project's database; change only the database name at the end. Treat the password like the instance's master key. Now any Postgres tool on your machine can use it:

```bash
psql "postgresql://postgres:$DB_PASSWORD@127.0.0.1:5432/p_app_one"

pg_dump -Fc -f app-one.dump \
  "postgresql://postgres:$DB_PASSWORD@127.0.0.1:5432/p_app_one"
```

If you already run Postgres locally on 5432, forward a different local port and use it in the URL:

```bash
ssh -N -L 6543:127.0.0.1:5432 supahost@acme.supahost.space
psql "postgresql://postgres:$DB_PASSWORD@127.0.0.1:6543/p_app_one"
```

## ORMs and migration tools

Prisma, Drizzle, Flyway and the Supabase CLI's database commands all work through the tunnel, because to them it's an ordinary Postgres connection. Two things to keep in mind:

- **The tunnel runs on your machine.** It's for development, migrations and admin work. Your deployed app servers shouldn't depend on an SSH tunnel; they should use the Data API with supabase-js. See [Prisma](/docs/integrations/prisma) and [Drizzle ORM](/docs/integrations/drizzle).
- **There's no connection pooler.** The instance's Postgres allows 400 connections in total, shared by every project and by the Supabase services. Keep ORM pool sizes small. See [Connections and timeouts](/docs/database/connections-and-timeouts).

## Troubleshooting

- **Permission denied (publickey):** the key may not be active yet (allow a minute), or your SSH client is offering a different key. Try `ssh -i ~/.ssh/id_ed25519 supahost@acme.supahost.space`.
- **Connection refused or timed out on port 22:** check the instance is on Business or Enterprise and at least one key is added.
- **`password authentication failed` through the tunnel:** copy the connection string again from **Show connection string**. See [FATAL: password authentication failed](/kb/password-authentication-failed).

## Related

- [Connecting directly to Postgres (psql, pg_dump, ORMs)](/kb/direct-postgres-connection)
- [Monitoring, health checks and alerts](/docs/platform/monitoring)
- [Security at SupaHost](/docs/security/overview)
