Steps with Docker
-
Install Docker
Docker's convenience script installs it in one command. Docker describes it as meant for development environments; for a server you will keep for years, use the apt repository from the Docker install guide instead.
$ curl -fsSL https://get.docker.com | sh $ sudo docker --version
-
Make a password
Hex characters need no escaping in a connection string. Keep the password in a variable for the next step and save it somewhere safe: you will need it again.
$ PGPASS=$(openssl rand -hex 18) $ echo "$PGPASS"
-
Start Postgres
The official
postgresimage creates the user and the database from environment variables.POSTGRES_PASSWORDis required. From Postgres 18 the volume mounts at/var/lib/postgresql(for 17 and earlier, at/var/lib/postgresql/data). Publishing the port on127.0.0.1keeps it off the internet.$ sudo docker run --name pg -d --restart unless-stopped \ -e POSTGRES_USER=app -e POSTGRES_DB=app \ -e POSTGRES_PASSWORD="$PGPASS" \ -v pgdata:/var/lib/postgresql \ -p 127.0.0.1:5432:5432 \ postgres:18 -
Check that it answers
Run
psqlinside the container.$ sudo docker exec -it pg psql -U app -d app -c "select version();" -
Give the app its connection string
The format is
postgresql://user:password@host:port/dbname, and 5432 is the default port. Put it in a.envfile next to the app and keep that file out of git.$ echo "DATABASE_URL=postgresql://app:[email protected]:5432/app" >> .env $ chmod 600 .env $ echo ".env" >> .gitignore
-
Make a backup before you need one
A custom-format dump is compressed and restores one object or the whole database. Copy it off the server, for example to object storage.
$ sudo docker exec pg pg_dump -U app -F c app > app.dump # restore into a fresh database: $ sudo docker exec pg createdb -U app newdb $ sudo docker exec -i pg pg_restore -U app -d newdb < app.dump
Check that it works
Restart the container and look for your data. If a table you made is still there, the volume works.
$ sudo docker exec pg psql -U app -d app -c "create table hello (id int);" $ sudo docker restart pg $ sudo docker exec pg psql -U app -d app -c "\dt"
Docker, managed, or Uno
Docker on your own server costs nothing extra but is yours to look after. A managed Postgres from a hosting provider takes that work away for a monthly fee. On Uno the database is a managed one that the agent can create.
| Docker on your server | Managed database at a host | Managed on Uno | |
|---|---|---|---|
| Who creates it | You, with one command | You, in the host's console | The agent, after you approve |
| The password | You make and store it | The host makes it, you copy it | The connection string goes into the project's .env as DATABASE_URL, mode 0600, and the agent never sees the password |
| Who can reach it | You choose: 127.0.0.1 or a public port | Usually a public address with TLS | Reachable from your account's Uno computers on an internal address, not from the internet |
| Backups | You run pg_dump yourself | The host's schedule | A backup is a copy of the whole disk, not a pg_dump file; the schedule is optional |
| Where it runs | On your server, sharing its memory | On the host's machine | A managed Postgres database runs on its own small Uno computer and counts against your plan |
| Version | The image tag you choose; 18 is the latest | The host's list | PostgreSQL 16 |
The short way: ask the agent
In a chat in Uno Work, say "create a Postgres database for this app". You approve the request. The database is ready in a minute or two. The connection string goes into the project's .env as DATABASE_URL, mode 0600, and the agent never sees the password. You skip steps 1–5. The size is yours to set: Memory from 512 MB to 16 GB and disk from 5 to 500 GB, with defaults of 1 GB and 10 GB.
Plus: $20 a month
What the Uno database does not do
- A database needs a paid plan; the free trial computer is refused.
- It is not reachable from the internet, so you cannot point a tool on your laptop at it directly. Your apps on Uno computers in the same account can.
- A managed Postgres database runs on its own small Uno computer and counts against your plan.
- Backups: A backup is a copy of the whole disk, not a pg_dump file; the schedule is optional. Make your own
pg_dumptoo if you want a file you can move to another host. - You delete a database in the Uno console, not from the chat.
Pick the computer your app and its database live in
Every plan sleeps when idle and wakes on the first request. The database needs a paid plan; move up any time.
Small
A Telegram bot, a VPN, a small backend
$5/ month
- 1 vCPU · 1 GB
- 15 GB disk
- 25 GB cloud storage
- 5 boosts a month
Plus
Agents that work with your laptop closed
$20/ month
- 4 vCPU · 4 GB
- 40 GB disk
- 100 GB cloud storage
- Uno Work in the cloud
Pro
A product in production, a second MacBook
$70/ month
- 16 vCPU · 16 GB
- 120 GB disk
- 300 GB cloud storage
- Speed of a MacBook Air M4
Max
A team and many agents at once
$200/ month
- 32 vCPU · 48 GB
- 300 GB disk
- 1,024 GB cloud storage
- 5 public IPv4
Add Uno AI to any plan: 40 h of AI work a month on Small, 100 h on Plus, unlimited on Max. Today: GLM-5.3 Flash and MiMo v2.6 Pro. Or sign in with your own Claude or ChatGPT.
Common problems
"password authentication failed for user"
POSTGRES_PASSWORD is read only the first time the volume is empty. If you started the container before, with another password, the old one still applies. Remove the volume with docker volume rm pgdata (this deletes the data) or change the password with ALTER USER.My data is gone after I recreated the container
/var/lib/postgresql; 17 and earlier under /var/lib/postgresql/data. Mount the path that matches your image tag."port is already allocated" or "address already in use"
-p 127.0.0.1:5433:5432, and use 5433 in the connection string.I cannot connect from my laptop
127.0.0.1: only the server itself can reach the port. From your laptop, forward it over SSH: ssh -N -L 5432:127.0.0.1:5432 user@server, then connect to 127.0.0.1:5432 on the laptop.A character in my password breaks the connection string
@, / and : have to be percent-encoded in a URI. A hex password from openssl rand -hex has none of them.