
Every database admin eventually hits the same wall. You spin up a fresh PostgreSQL instance on a managed platform, it’s ready in under a minute, and then you need to actually look inside it. The usual options are ugly: open a port to the public internet and pray, dig an SSH tunnel that breaks every time your laptop sleeps, or install a fat desktop client on five different machines that all need updating separately. None of those scale well once you’re managing more than a couple of environments, and all of them add attack surface you didn’t need.
LibreDB Studio flips that model. Instead of installing a client on every workstation that needs database access, you deploy the tool next to the data itself, as a container, a systemd service, or a Helm release, and everyone reaches it through a browser tab. Sixteen database engines share one interface, from PostgreSQL and MySQL to Oracle, MongoDB, ClickHouse, and Cassandra, which matters if your infrastructure is the typical polyglot mess most production environments end up becoming after a few years of acquisitions and “temporary” tooling decisions.
This walkthrough covers installing LibreDB Studio on Ubuntu 26.04 LTS, codenamed Resolute Raccoon, Canonical’s newest long-term support release that shipped in April 2026 with kernel 7.0, GNOME 50, and a Wayland-only desktop stack. Whether you’re running it on a bare-metal box in a rack, a cloud VM behind a load balancer, or a Kubernetes cluster, the steps below reflect how this actually gets deployed on production systems, not just a sanitized quick-start.
We’ll cover Docker deployment (the recommended path for most shops), the native .deb package with systemd integration, Snap for teams that prefer confined packages, reverse proxy configuration with Nginx, firewall rules, and the troubleshooting scenarios that actually show up when you’re the one paged at 2 a.m.
What LibreDB Studio Actually Is
Before touching a terminal, it’s worth being precise about what you’re installing. LibreDB Studio is an MIT-licensed, self-hosted SQL IDE that runs as a browser application rather than a desktop program. It’s built on Next.js and uses the Monaco editor, the same engine that powers VS Code, so the autocomplete and syntax highlighting feel familiar immediately.
The interesting architectural decision is where it lives. Rather than installing on a developer’s laptop and reaching outward to your database, Studio deploys next to the database, inside the same private network, and your team connects to it over HTTPS. Nothing has to face the public internet. That single design choice removes an entire category of exposed-database incidents that show up in breach reports every year.
It supports PostgreSQL, MySQL, Oracle, SQL Server, SQLite, libSQL, DuckDB, MongoDB, Redis, Couchbase, ClickHouse, Apache Druid, Elasticsearch, OpenSearch, Apache Trino, and Apache Cassandra natively, and through wire-protocol compatibility it also talks to MariaDB, TiDB, CockroachDB, YugabyteDB, Valkey, DragonflyDB, and roughly two dozen other engines that speak one of those sixteen protocols.
Prerequisites Before You Start
A few things you need sorted before running any install command:
- A clean Ubuntu 26.04 LTS host, either fresh or already provisioned, with
sudoaccess. - At least 1 GB of RAM free for the container or process; 2 GB is safer if you’ll run the AI agent features against a local Ollama model.
- Port 3000 available, or a plan to remap it behind a reverse proxy.
- A domain name pointed at the server if you intend to serve this over HTTPS in production, which you should.
- Docker Engine installed if you’re going the container route, or Node.js 24+ if you’re going the npx route.
Run a quick sanity check on your system before anything else:
lsb_release -a
uname -r
free -h
df -h /
You want to confirm you’re actually on 26.04 (kernel 7.0 series), have enough free memory, and aren’t about to install onto a disk sitting at 95% capacity. That last one sounds obvious, but it’s the single most common reason Docker pulls fail silently on production boxes nobody’s monitored in a while.
Method 1: Docker Installation (Recommended)
Docker is the path most teams should take, and for good reason: it isolates the Node.js runtime from your host, makes upgrades a one-line pull-and-restart, and matches how the LibreDB team ships and tests the product.
Step 1: Install Docker Engine on Ubuntu 26.04
Ubuntu 26.04 doesn’t ship Docker in the default repos at a version you’d want in production, so pull it from Docker’s own repository.
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Add your user to the docker group so you’re not typing sudo before every command, which also matters for any automation scripts that run under a service account later:
sudo usermod -aG docker $USER
newgrp docker
Verify it’s actually working:
docker run hello-world
Step 2: Pull and Run LibreDB Studio
The zero-config quick start works fine for a quick test, but for anything you plan to keep around, set explicit credentials instead of letting the container generate ones you’ll have to dig out of the logs later.
docker run -d \
--name libredb-studio \
--restart unless-stopped \
-p 3000:3000 \
-e ADMIN_EMAIL=admin@yourdomain.com \
-e ADMIN_PASSWORD='ChangeThisImmediately!2026' \
-e USER_EMAIL=analyst@yourdomain.com \
-e USER_PASSWORD='AnotherStrongPassword2026' \
-e JWT_SECRET="$(openssl rand -base64 32)" \
ghcr.io/libredb/libredb-studio:latest
That --restart unless-stopped flag matters more than it looks. It’s the difference between a container that survives a reboot and one that just sits there dead the next time you check on the server, which is exactly the kind of thing that gets discovered during an incident rather than during a calm afternoon.
Confirm the container is healthy:
docker ps --filter name=libredb-studio
docker logs libredb-studio --tail 50
Open http://your-server-ip:3000 in a browser and log in with the admin credentials you set. If the credentials weren’t set explicitly, they’ll be printed once in the container’s startup logs, which is why grabbing them from docker logs immediately matters if you went the zero-config route.
Step 3: Persist Data with Docker Compose
Running a bare docker run is fine for testing, but production deployments should use Compose so restarts, volume mounts, and environment variables are version-controlled rather than living in your shell history.
# docker-compose.yml
services:
libredb-studio:
image: ghcr.io/libredb/libredb-studio:latest
container_name: libredb-studio
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
ADMIN_EMAIL: admin@yourdomain.com
ADMIN_PASSWORD: ${LIBREDB_ADMIN_PASSWORD}
USER_EMAIL: analyst@yourdomain.com
USER_PASSWORD: ${LIBREDB_USER_PASSWORD}
JWT_SECRET: ${LIBREDB_JWT_SECRET}
volumes:
- libredb-data:/app/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 5s
retries: 3
volumes:
libredb-data:
Notice the port binding is 127.0.0.1:3000:3000, not 0.0.0.0:3000:3000. Binding to loopback only means the app isn’t directly reachable from outside the box, which is exactly what you want once Nginx is sitting in front of it doing TLS termination. This one habit prevents a surprising number of “why is my database admin panel indexed by Google” incidents.
Store secrets in a .env file next to the compose file, and make sure that file has restrictive permissions:
chmod 600 .env
docker compose up -d
Method 2: Native .deb Package with systemd
If you’d rather avoid Docker entirely, maybe your compliance team hasn’t signed off on containers yet, the .deb package installs LibreDB Studio as a proper systemd service, which some ops teams simply prefer for consistency with everything else on the box.
Step 1: Download and Install the Package
Grab the latest release from GitHub, matching your architecture (amd64 for standard cloud instances, arm64 for Graviton or Ampere-based servers):
curl -LO https://github.com/libredb/libredb-studio/releases/latest/download/libredb-studio_amd64.deb
sudo dpkg -i libredb-studio_amd64.deb
If dpkg complains about missing dependencies, which happens more often than package maintainers like to admit, resolve them immediately after:
sudo apt --fix-broken install
Step 2: Enable and Start the Service
The package ships a systemd unit out of the box.
sudo systemctl enable --now libredb-studio
sudo systemctl status libredb-studio
Check the logs the same way you’d check any other systemd-managed service:
journalctl -u libredb-studio -f
That -f flag tails the log live, which is genuinely useful during the first few minutes after install when you want to catch a startup error before it becomes a support ticket. Configuration for the .deb install typically lives under /etc/libredb-studio/ or is passed via environment file referenced in the systemd unit, so check /etc/systemd/system/libredb-studio.service if you need to adjust environment variables like the JWT secret or admin credentials, then reload:
sudo systemctl daemon-reload
sudo systemctl restart libredb-studio
Method 3: Snap Package
Snap is worth mentioning because Ubuntu ships it natively and some organizations standardize on Snap for anything not container-based, largely for the confinement and automatic update model.
sudo snap install libredb-studio
Credentials on first run get printed to the Snap log rather than a standard systemd journal entry:
sudo snap logs libredb-studio
Snap’s confinement model means the app runs with restricted filesystem access by default, which is good for security but occasionally trips people up if they expect it to reach arbitrary paths on the host. If you hit permission errors reading a local SQLite file, that’s usually the confinement talking, not a bug in the app.
Configuring Nginx as a Reverse Proxy
Running LibreDB Studio directly on port 3000 without TLS is fine for a lab, but never acceptable for production. Here’s a working Nginx config that terminates TLS and proxies to the local container.
sudo apt install -y nginx certbot python3-certbot-nginx
Create a server block:
# /etc/nginx/sites-available/libredb-studio
server {
listen 80;
server_name dbstudio.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 90s;
}
}
The Upgrade and Connection headers matter here because Studio’s live monitoring dashboards and query execution rely on WebSocket-style connections for real-time updates. Skip those headers and you’ll get a UI that loads but never refreshes data, which is a confusing failure mode to debug if you don’t know to look there first.
Enable the site and get a certificate:
sudo ln -s /etc/nginx/sites-available/libredb-studio /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d dbstudio.yourdomain.com
nginx -t before reloading isn’t optional habit, it’s the thing that saves you from taking down every site on a shared Nginx instance because of one typo in a new config block.
Firewall and Security Hardening
Since traffic now flows through Nginx on 443, lock down direct access to port 3000 and the app port itself.
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw deny 3000
sudo ufw enable
sudo ufw status verbose
A few things worth doing beyond the basic firewall rule:
- Rotate the
JWT_SECRETon a schedule, and never reuse the same secret across environments (staging and production sharing a JWT secret is a classic way to accidentally let a staging token authenticate against prod). - Set up OIDC single sign-on through Keycloak, Auth0, or Azure AD instead of relying on local email/password accounts once more than a handful of people need access. LibreDB Studio ships this under the same MIT license, no paywall.
- Enable the audit trail feature and actually review it periodically. Every executed query gets logged, which matters enormously the first time someone runs an unintended
DELETEagainst production and you need to know exactly what happened and who did it. - Keep the container or package updated. Run
docker pull ghcr.io/libredb/libredb-studio:latest && docker compose up -don a schedule, or subscribe to release notifications on the GitHub repo. - If you’re connecting to remote databases, always enable SSL/TLS on the connection itself in addition to TLS on the Studio front end. The reverse proxy protects the browser-to-Studio hop; it does nothing for the Studio-to-database hop.
Performance Tuning for Production Use
On a modest 2-vCPU / 4 GB RAM instance, LibreDB Studio idles comfortably, but a few adjustments help under real usage, particularly once your team starts running large result sets through the virtualized data grid or profiling tables with millions of rows.
For disk I/O, if you’re running the container with a persistent volume for audit logs and saved queries, put that volume on an SSD-backed disk rather than spinning storage. The audit trail writes on every query execution, and on a busy team that’s a steady trickle of small writes, exactly the workload pattern that punishes slow disks hardest.
For memory, if multiple analysts are running the AI Data Profiler against wide tables simultaneously, bump the container’s memory limit:
docker update --memory 4g --memory-swap 4g libredb-studio
For network, if your database lives in a different availability zone or region than the Studio instance, latency compounds fast with large result sets. Deploy Studio in the same network segment as the database whenever the topology allows it. This is the whole point of the “deploys next to the data” architecture, and skipping it defeats half the performance benefit.
For CPU, the Node.js server is largely single-threaded for request handling, so vertical scaling helps less than you’d expect past a certain point. If you’ve got dozens of concurrent users hammering it, consider running multiple Studio instances behind a load balancer rather than throwing more cores at one container.
Troubleshooting Common Issues
Container starts then immediately exits. Check docker logs libredb-studio first. The most common cause is a missing or malformed JWT_SECRET. The app expects a proper base64 string, not an empty value or a placeholder like change-me.
“Connection refused” when connecting to a database from within Studio. Nine times out of ten this is a Docker networking issue, not a Studio bug. If your database runs in another container, make sure both are on the same Docker network:
docker network create dbnet
docker network connect dbnet libredb-studio
docker network connect dbnet your-postgres-container
Then connect using the container name as the host, not localhost.
Login page loads but authentication fails with valid credentials. This usually means the ADMIN_PASSWORD environment variable set at container creation doesn’t match what you’re typing, often because a previous run generated random credentials and you’re still using those old ones from the log. Recreate the container with explicit env vars rather than trying to patch a running one, since credentials are typically set at first boot only.
Nginx returns 502 Bad Gateway. Confirm the container is actually listening on 3000:
sudo ss -tlnp | grep 3000
curl -I http://127.0.0.1:3000
If nothing’s listening, the app crashed after Nginx started, check the container logs again. If something is listening but Nginx still 502s, check SELinux or AppArmor policies, which occasionally block proxy connections on hardened Ubuntu images, especially ones provisioned from a CIS-hardened base template.
WebSocket features (live monitoring, real-time query status) don’t update. Almost always the missing Upgrade/Connection headers in the Nginx config covered above. Double check they’re present and that no upstream CDN or load balancer is stripping them.
High memory usage over time. If the container’s memory climbs steadily during a long session with the AI Data Profiler running against very wide tables, that’s expected behavior with the virtualized grid holding large result sets in memory. Restarting the container clears it; setting a hard --memory limit with --restart unless-stopped means a runaway session gets killed and restarted automatically rather than taking down the host.