Spaces:
Paused
Paused
File size: 6,222 Bytes
0ebe358 8003382 0ebe358 8003382 0ebe358 8003382 8dec6cd 8003382 8dec6cd 8003382 8dec6cd 8003382 8dec6cd 8003382 8dec6cd 8003382 8dec6cd 8003382 8dec6cd 8003382 8dec6cd 8003382 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | ---
title: Remote Postgres
emoji: π
colorFrom: indigo
colorTo: blue
sdk: docker
app_port: 7860
pinned: false
---
# Remote Postgres on Hugging Face Spaces
A single Docker container that runs **PostgreSQL** + a small **web UI** that hands you a
**public connection URL** to drop into any demo project that needs a Postgres database.
Because Hugging Face Spaces only exposes one HTTP port (no raw TCP), the container opens an
outbound **TCP tunnel** so Postgres is reachable from anywhere with a normal `postgresql://β¦`
URL and any client/ORM. The default tunnel is **[bore](https://github.com/ekzhang/bore)** β
open-source, **no signup, no token**. Set `NGROK_AUTHTOKEN` to use ngrok instead.
> β οΈ **Throwaway by default.** The public URL changes on every restart. Data is **backed up**
> (see Persistence) but treat the cluster itself as disposable.
---
## Deploy (5 minutes)
1. **Create a Space** β New Space β **SDK: Docker** β **Visibility: Public** (so keep-alive can
reach it; the UI is still password-locked).
2. **Push these files** to the Space repo (or upload them in the web UI):
`Dockerfile`, `start.sh`, `README.md`, and the `app/` folder.
3. **Add Space secrets** (Settings β *Variables and secrets*):
| Secret | Required | Purpose |
|---|---|---|
| `APP_PASSWORD` | recommended | Locks the web UI (Basic auth, user `admin`) so only you see the URL |
| `HF_TOKEN` + `HF_BACKUP_REPO` | for backups | Off-Space dumps to a private HF Dataset (see Persistence) |
| `POSTGRES_PASSWORD` | recommended | Fixed DB password (otherwise one is generated per boot) |
| `NGROK_AUTHTOKEN` | optional | Use ngrok instead of bore for the tunnel |
| `POSTGRES_USER` / `POSTGRES_DB` | optional | Defaults: `demo` / `demo` |
4. The Space builds and starts. Open it β the UI shows your **connection URL** + a Copy button.
## Use it
Open the Space, copy the `postgresql://β¦` URL, and use it anywhere:
```bash
psql "postgresql://demo:PASSWORD@bore.pub:26134/demo"
```
```python
import psycopg
conn = psycopg.connect("postgresql://demo:PASSWORD@bore.pub:26134/demo")
```
Works the same with SQLAlchemy, Prisma, Drizzle, node-postgres, etc.
## Endpoints
- `/` β **Gradio uptime-watcher dashboard**: live Postgres/tunnel status, container uptime,
availability %, recent-checks table, keep-alive counter, and the copyable connection URL.
Login-protected (user `admin`) when `APP_PASSWORD` is set.
- `/api/connection` β JSON with the URL + fields (also protected).
- `/keepalive?src=<label>` β open; bumps the keep-alive counter and logs the hit. Hit by the
in-container loop (`src=self`) and the external cron (`src=github-actions`).
- `/health` β open status check: `{"status","postgres","tunnel","uptime"}`.
## Don't lose the data (persistence)
The container stacks two independent safeguards β use either or both:
1. **Persistent volume (cleanest).** Enable **persistent storage** on the Space
(Settings β Storage). It mounts a writable `/data`; on boot the container detects it and
stores the live Postgres cluster at `/data/pgdata`, so data survives restarts as-is. Zero
extra config. (Paid HF add-on.)
2. **Backups + auto-restore (works on free tier).** Every `BACKUP_INTERVAL_MIN` minutes the
DB is `pg_dump`ed to `BACKUP_DIR`, and β if you set `HF_TOKEN` + `HF_BACKUP_REPO` β mirrored
to a **private HF Dataset repo** (durable off-Space). On a fresh boot the container
**auto-restores the latest dump**, so even a wiped ephemeral Space comes back with your data
(you lose at most the last interval). There's also a **Back up now** button on the dashboard.
To enable off-Space backups, add Space secrets:
| Secret | Example | Purpose |
|---|---|---|
| `HF_TOKEN` | `hf_xxx` (write) | Lets the Space push/pull dumps β [create one](https://huggingface.co/settings/tokens) |
| `HF_BACKUP_REPO` | `your-name/pg-backups` | Private dataset repo (auto-created) holding the dumps |
Optional: `BACKUP_INTERVAL_MIN` (default 30), `BACKUP_KEEP` (default 24 local dumps).
> Recommended combo for "I don't want to risk it" on free tier: **HF Dataset backups every
> 15β30 min** + the external hourly keep-alive below. Data is safe across restarts; only the
> ngrok URL still rotates (re-copy it from the dashboard after a restart).
## Keeping the Space awake
Free Spaces sleep after **48h with no HTTP traffic to the web app**. Your Postgres queries go
through ngrok and do **not** count β only requests to this app's web port do. Two layers keep
it awake:
1. **In-container self-ping (automatic).** A loop hits `https://$SPACE_HOST/keepalive` every
20 min and logs each hit. Keeps the Space from ever going idle *while the container runs* β
but it cannot wake a Space that already slept (it's asleep too).
2. **External hourly cron (recommended).** An outside request also **wakes** a slept Space.
Pick one:
- **GitHub Actions** β [`.github/workflows/keepalive.yml`](.github/workflows/keepalive.yml)
is included. Push this repo to GitHub, add a repo secret **`SPACE_URL`** =
`https://<your-space>.hf.space`, and it pings every hour. (Note: GitHub disables scheduled
workflows after 60 days with no commits β push occasionally, or use the option below.)
- **No-code:** [cron-job.org](https://cron-job.org) or [UptimeRobot](https://uptimerobot.com)
β GET `https://<your-space>.hf.space/keepalive` every hour. Truly set-and-forget.
You can watch the **Keep-alive hits** counter on the dashboard to confirm pings are landing.
> A sleep/wake or any rebuild still wipes data and rotates the URL β keep-alive only keeps the
> Space *running*; it does not make the data or the URL permanent.
## Keep it secure
- Set the Space to **Private** and set **`APP_PASSWORD`** so the URL isn't world-readable.
- The DB itself is internet-exposed via the tunnel β anyone with the URL **and** password can
connect. Use a strong `POSTGRES_PASSWORD` and treat the data as disposable.
## Local test
```bash
docker build -t remote-pg .
docker run -p 7860:7860 \
-e NGROK_AUTHTOKEN=your_token \
-e APP_PASSWORD=letmein \
remote-pg
# open http://localhost:7860
```
|