LibreDB Studio Field Guide
v0.17.0 MIT licensed 18 engines

Install, then a tour of every screen

One browser tab for every database you already run

Una pestaña del navegador para todas las bases de datos que ya tienes

すでに動かしているすべてのデータベースを、ブラウザーのタブ一つで

Одна вкладка браузера для всех баз данных, которые у вас уже работают

آپ کے پاس پہلے سے چل رہے تمام ڈیٹابیس، براؤزر کے ایک ہی ٹیب میں

一个浏览器标签页,管住你手上所有数据库

一個瀏覽器分頁,管住你手上所有資料庫

Jedna karta przeglądarki do każdej bazy danych, którą i tak już utrzymujesz

Un onglet de navigateur pour toutes les bases de données que vous exploitez déjà

Una scheda del browser per ogni database che già fai girare

Ein Browser-Tab für jede Datenbank, die du ohnehin schon betreibst

Satu tab browser untuk semua database yang sudah Anda jalankan

Uma aba do navegador para cada banco de dados que você já roda

LibreDB Studio is a self-hosted SQL IDE. You point it at databases you already have, and it gives you a schema explorer, a real SQL editor, a data grid you can edit in place, charts, ER diagrams, a query audit trail and a read-only database agent. All of it under MIT, with nothing held back for a paid tier.

Version covered
0.17.0Everything in this guide was checked against a running 0.17.0 container.
Runs as
One containerPort 3000 inside. No sidecar to run, though on first boot it does seed two sample databases into its data directory.
Engines with a driver
18Reaching 46 named engines in all, once the engines that speak the same wire protocol are counted.
Footprint
~384 MB heapThe image sets a Node heap cap of 384 MB, so it fits on a small NAS or a 1 GB droplet.

Install

Pick where you are running it

Choose one. The rest stay out of your way.

Docker Compose Railway Fly.io Render Koyeb DigitalOcean AWS Azure Google Cloud Helm Kubero Sealos OperatorHub Rancher Unraid TrueNAS SCALE CasaOS YunoHost 1Panel CapRover Dokploy Cosmos Proxmox Your own machine

Docker Compose

Any Linux, macOS or Windows host with Docker. Five minutes.

Docker Compose is the path I used to capture this guide, so it is the one described in most detail.

What you need

Docker with the Compose plugin, and a free port. That is the whole list. You do not need to clone the repository, and nothing is built from source.

The shortest possible start

If you only want to look at it, one command is enough:

docker run -p 3000:3000 ghcr.io/libredb/libredb-studio:0.17.0

Open http://localhost:3000. On a first run with no credentials set, the app generates an administrator password and prints it to the container log. Read it with docker logs.

The version you actually want to keep

For anything that outlives the afternoon, give it a compose file so the settings and the stored connections survive a restart. This is close to the stack I ran while writing the guide:

# compose.yaml
services:
  studio:
    image: ghcr.io/libredb/libredb-studio:0.17.0
    container_name: libredb-studio
    ports: ["3000:3000"]
    environment:
      ADMIN_EMAIL: admin@libredb.org
      ADMIN_PASSWORD: choose-something-long
      JWT_SECRET: "CHANGE_ME"   # openssl rand -base64 32
    volumes:
      - studio-data:/app/data
    restart: unless-stopped

volumes:
  studio-data:

The JWT_SECRET above is deliberately too short to work. Paste the real output of openssl rand -base64 32 in its place. Leave the placeholder and the app refuses to start and tells you why: JWT_SECRET is too short. That refusal is the behaviour you want, because a secret printed in a guide is a secret everybody has.

docker compose up -d
docker compose logs -f studio

Which image to pin

The image line above names the default tag, and two siblings are published beside it. All three are the same application. ghcr.io/libredb/libredb-studio:0.17.0 is built on Debian and carries every driver, including Oracle's Thick mode, which needs Oracle Instant Client and has no musl build. :0.17.0-alpine is the same product on musl and gives up only that: Oracle Thin still connects. :0.17.0-alpine-slim is the smallest and the only one that trades a feature for size. It ships without the DuckDB driver, whose shared library is the largest removable thing in the image, and opening a DuckDB connection there does not fail as though the engine were unreachable: it answers DuckDB is not available in this deployment and then names the remedy: the libredb-studio -alpine-slim image leaves it out to stay small; the default and -alpine tags ship it. Pulled on the machine that wrote this page, Docker reports the three at 903 MB, 530 MB and 259 MB, so the smallest is under a third of the default; your own figures will differ with the architecture you pull for. latest, latest-alpine and latest-alpine-slim move with each release, and the immutable sha- tags carry the same suffixes. The Helm chart's default stays the Debian tag; set image.tag to pin a variant.

What you should see

The container picks its own bind address before Next.js prints anything, so the first line of the log is about the socket rather than the app. It prefers ::, which serves IPv4 and IPv6 through one listener, and prints http://[::1]:3000 for the local address rather than the word localhost. Set HOSTNAME to pin it. The log settles within a few seconds and the health endpoint answers:

libredb-studio: bind address :: (dual-stack verified)
▲ Next.js 16.3.4
- Local:         http://[::1]:3000
- Network:       http://[::]:3000
✓ Ready in 0ms

$ curl -s localhost:3000/api/db/health
{"status":"healthy","timestamp":"...","service":"libredb-studio"}

Then open http://localhost:3000, sign in with the email and password you set, and skip ahead to the tour.

Trap 1 of 2

Two lines are worth adding once you know you need them. Set AUTH_COOKIE_SECURE to false if you will reach the app over plain HTTP on anything that is not localhost, and set it back to true the day HTTPS sits in front. Set STORAGE_PROVIDER to sqlite if more than one person will use it, so saved connections live on the volume instead of in one browser.

Trap 2 of 2

Three paths answer a liveness probe in the same shape, differing only in the timestamp they carry: /health, /api/health and /api/db/health. Point your orchestrator at any of them. Most of /api/ is not a substitute: some paths redirect to the login screen and answer 200 from there, which tells you nothing about the app, while others answer 401 or serve unrelated data. A GET at the health path answers the liveness question, not the readiness one; it reports that the service is up without opening a database connection. A POST to /api/db/health, with a connection in the body, is the one that actually reaches a database; the other two answer 405 to a POST.

Two more things worth setting

JWT_SECRET has to be at least 32 characters. A shorter value is fatal on purpose: the server refuses to start rather than serve a deployment where every login fails. On the server-side stores, sqlite and postgres, it also derives the key that encrypts saved database passwords, so rotating it leaves those connections in place with the password field dropped. Set STORAGE_ENCRYPTION_KEY if you want to rotate one without disturbing the other.

STORAGE_PROVIDER decides where saved connections live. The default, local, keeps them in the browser, which is fine for one person and useless for a team. sqlite puts them on the volume. postgres puts them in a database you name with STORAGE_POSTGRES_URL. Worth knowing before you decide: credential encryption is installed on the server-side stores only, so under the default local the saved passwords sit in the browser in plain text.

Worth knowing before you need it: a local account can require a six-digit code as well as its password. Set ADMIN_TOTP_SECRET to a base32 secret and the password alone stops being enough, which the login answers as mfaRequired until the code arrives. USER_TOTP_SECRET does the same for the second account. Both are optional and nothing changes if you leave them out.

Two groups of variables the other panels never need are worth naming here, because every deployment on this page ends in a login page on an address somebody else can reach. The first is the rate limiter, five buckets, each a count and a window in seconds: failed logins per client address (RATE_LIMIT_LOGIN_MAX, RATE_LIMIT_LOGIN_WINDOW_SEC) default five per three hundred seconds; failed logins per submitted account, twenty per three hundred; AI and agent requests per signed-in user, twenty per sixty; database-reaching requests per user, a hundred and twenty per sixty; and audit lines for refusals, five per three hundred. Setting a count to zero disables that bucket. The second group decides what the limiter counts against: TRUST_PROXY_HEADERS defaults to true and reads the client address from X-Forwarded-For, and TRUSTED_PROXY_HOPS defaults to zero, which takes the leftmost entry, the one the caller writes. Behind a proxy, set it to the number of proxies in front, or a caller picks their own bucket and the audit log's address field is fiction.