Docker Deployment
The recommended way to run QueryGlow. ./queryglow install detects your server setup and configures SSL, the login and Safe Mode. Afterwards the same command updates, backs up and checks your install.
Prerequisites
- Docker with the compose plugin
Install:
curl -fsSL https://get.docker.com | sh - A regular user with sudo, in the docker group
The installer refuses to run as root. Run
sudo usermod -aG docker $USER, then log out and back in. - Linux server with 512 MB+ RAM (1 GB recommended) and 10 GB free disk
Ubuntu 20.04+, Debian 11+ or similar
- dig, curl, openssl and htpasswd
Checked by the installer, which offers to install them on Debian and Ubuntu
- Ports 80 and 443 open
Required for the Let's Encrypt certificate
- Domain pointing to your server
DNS A record:
db.yourcompany.com → your-server-ip
Auto-Detected Deployment Modes
The installer detects your server configuration:
Standalone Mode
Server without a running nginx
→ Docker runs: QueryGlow + nginx + certbot
Integration Mode
Server already has nginx running
→ Docker runs: QueryGlow only (uses your nginx and system certbot)
Method 1: The Installer (Recommended)
./queryglow install runs deploy.sh, the interactive installer. It handles the Docker configuration, the nginx reverse proxy, the Let's Encrypt certificate, Basic Auth and Safe Mode, and records the install for update, status, backup and doctor. Running ./deploy.sh directly still works.
# 1. Clone the repository
git clone [email protected]:mxfschr/queryglow.git
cd queryglow
# 2. Install
./queryglow installWhy SSH ([email protected]) instead of HTTPS?
GitHub disabled password authentication in 2021. If you try HTTPS, you'll be prompted for a password, but your actual GitHub password will NOT work. SSH keys are the simplest solution.
❌ Getting "Permission denied (publickey)" error?
This means your SSH key isn't set up with GitHub. Here's how to fix it:
- Check if you have an SSH key:
ls -la ~/.ssh/id_*.pub - If no key exists, create one:
ssh-keygen -t ed25519 -C "[email protected]" - Copy your public key:
cat ~/.ssh/id_ed25519.pub - Add to GitHub: Go to github.com/settings/keys → New SSH key → Paste your key
- Test the connection:
ssh -T [email protected]
🔐 Corporate network blocking SSH? Use HTTPS with a token
If your network blocks port 22 (SSH), you can use HTTPS with a Personal Access Token:
- Generate a token: Go to github.com/settings/tokens → Generate new token (classic) → Select
reposcope → Generate - Copy the token immediately (it's only shown once!)
- Clone using the token:
git clone https://github.com/mxfschr/queryglow.gitUsername: your GitHub username
Password: paste your token (NOT your GitHub password)
The installer asks for:
- Domain: for example
db.yourcompany.com - Email: for the SSL certificate
- Username: for the Basic Auth login
- Password: at least 8 characters
- AI API keys: optional (OpenAI, Anthropic or Google), or later in the app under Ask AI, AI providers
- Query Editor mode: Safe (recommended) or Power
What gets configured automatically:
- ✓ HTTPS with Let's Encrypt
- ✓ nginx reverse proxy
- ✓ Basic Auth login (htpasswd)
- ✓ Safe Mode enabled
- ✓ AES-256-GCM credential encryption
- ✓ Search engine blocking
- ✓ Security headers (HSTS in standalone mode)
- ✓ Auto SSL renewal
Day Two Commands
Both deployment modes, run in your QueryGlow checkout:
./queryglow status # what is installed and running
./queryglow logs -f # follow the app log
./queryglow update # back up, update, verify, roll back on failure
./queryglow update --check # is a newer release available?
./queryglow update --dry-run # print what an update would do
./queryglow update --tag v1.3.0 # update or downgrade to one release
./queryglow backup # snapshot .env, app data and nginx files
./queryglow restore backups/<ts> # put the app data from a snapshot back
./queryglow rollback # return to the release in the newest snapshot
./queryglow doctor # check the host; --fix repairs, --report for support
./queryglow version # installed and running versionHow updating works, step by step.
Plain Docker commands (logs, restart)
Standalone mode
docker compose -f docker-compose.production.yml logs -f
docker compose -f docker-compose.production.yml restartIntegration mode
docker logs queryglow -f
docker restart queryglowUpdate with ./queryglow update, not with a plain git pull and rebuild: nginx uses a rendered config that a pull does not update.
Integration Mode: What Your nginx Site Needs
In integration mode the installer writes the site file once. Updates never touch your system nginx, so a site file from an older install, or one you maintain by hand, needs these four blocks inside its server block for port 443 (use your app port instead of 3000):
# Agent Access (MCP): agents use their own Bearer token, not the Basic Auth
# password. Exact match, so no sibling path is exempt.
location = /api/mcp {
auth_basic off;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Connection "";
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 120s;
}
# Health probe for external monitors: status only, no version, no config.
location = /api/health {
auth_basic off;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Connection "";
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;
}
# Browser icons: Safari on iOS requests them without the login, and a 401
# here shows a second password prompt right after login.
location = /favicon.ico {
auth_basic off;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location ~ ^/apple-touch-icon(-[0-9]+x[0-9]+)?(-precomposed)?\.png$ {
auth_basic off;
return 204;
}Apply with sudo nginx -t && sudo systemctl reload nginx. ./queryglow doctor reports a site file that lacks the health or agent block.
Method 2: Manual Docker Compose
For local development, or if you want to manage nginx and SSL yourself. This runs QueryGlow only; you handle the reverse proxy.
1. Create the .env file
# Session secret (REQUIRED)
echo "SESSION_SECRET=$(openssl rand -hex 32)" > .env
# Salt for the key derivation (recommended for production)
echo "ENCRYPTION_SALT=$(openssl rand -hex 16)" >> .env
# Optional: an AI key in .env wins over a key entered in the app,
# so add a line only with a real key:
# echo "OPENAI_API_KEY=<your key>" >> .env
# Optional: Power Mode (not recommended)
# echo "QUERYGLOW_ALLOW_DESTRUCTIVE=true" >> .env2. Start with Docker Compose
# Development (no SSL, no login, port 3000 on this machine only)
docker compose up --build
# Production app container only, behind the nginx you run on the host
docker compose -f docker-compose.production.yml up -d --build queryglowName the queryglow service: without it the production file also starts its own nginx and certbot containers, which collide with a host nginx on ports 80 and 443.
3. Verify it's running
curl http://127.0.0.1:3000/api/health
# returns JSON with "status":"ok" plus timestamp and uptimeEnvironment Variables
| Variable | Required | Description |
|---|---|---|
| SESSION_SECRET | Yes | Encryption key for stored database passwords, SSH keys and AI keys entered in the app. Generate with openssl rand -hex 32 |
| ENCRYPTION_SALT | No | Salt for the key derivation. The installer writes a random one; generate with openssl rand -hex 16. Never change it once connections are saved. |
| QUERYGLOW_ALLOW_DESTRUCTIVE | No | false (default) = Safe Mode: blocks DROP TABLE, TRUNCATE and DELETE or UPDATE without WHERE in the Query Editor.true = Power Mode: also allows those statements. DROP DATABASE and statements that run commands or touch server files stay blocked. |
| PORT | No | App port. Default: 3000 |
| OPENAI_API_KEY | No | For Ask AI (OpenAI). A key here wins over one entered in the app. |
| ANTHROPIC_API_KEY | No | For Ask AI (Claude). A key here wins over one entered in the app. |
| GOOGLE_API_KEY | No | For Ask AI (Gemini). A key here wins over one entered in the app. |
The image runs on Node.js 22 as a non-root user. After editing .env, recreate the container with docker compose -f docker-compose.production.yml up -d --force-recreate queryglow (a plain restart does not read it again). See Environment Variables for the complete reference.
Backup and Restore
QueryGlow keeps its data (connections, saved queries, history, agent tokens and activity, AI settings, pinned SSH host keys) in the Docker volume queryglow-data, not in the data/ folder of the checkout.
./queryglow backup # .env, app data, nginx files to backups/<timestamp>/
./queryglow restore backups/20260925-081057 # put the app data back (asks first)Keep a copy of backups/ off the server. Without SESSION_SECRET and ENCRYPTION_SALT from .env, saved passwords and keys cannot be decrypted.
Security Notes
- • In production, QueryGlow binds to
127.0.0.1only and is not reachable from the internet directly. - • Always install with
./queryglow installfor production, so SSL and the login are configured. - • Never expose port 3000 directly to the internet without a reverse proxy and authentication.
- • Back up with
./queryglow backup: it stores.env(SESSION_SECRET and ENCRYPTION_SALT) and the app data.