Documentation

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 install

Why 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:

  1. Check if you have an SSH key:ls -la ~/.ssh/id_*.pub
  2. If no key exists, create one:ssh-keygen -t ed25519 -C "[email protected]"
  3. Copy your public key:cat ~/.ssh/id_ed25519.pub
  4. Add to GitHub: Go to github.com/settings/keys → New SSH key → Paste your key
  5. 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:

  1. Generate a token: Go to github.com/settings/tokens → Generate new token (classic) → Select repo scope → Generate
  2. Copy the token immediately (it's only shown once!)
  3. Clone using the token:git clone https://github.com/mxfschr/queryglow.git

    Username: 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 version

How 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 restart

Integration mode

docker logs queryglow -f
docker restart queryglow

Update 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" >> .env

2. 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 queryglow

Name 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 uptime

Environment Variables

VariableRequiredDescription
SESSION_SECRETYesEncryption key for stored database passwords, SSH keys and AI keys entered in the app. Generate with openssl rand -hex 32
ENCRYPTION_SALTNoSalt 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_DESTRUCTIVENofalse (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.
PORTNoApp port. Default: 3000
OPENAI_API_KEYNoFor Ask AI (OpenAI). A key here wins over one entered in the app.
ANTHROPIC_API_KEYNoFor Ask AI (Claude). A key here wins over one entered in the app.
GOOGLE_API_KEYNoFor 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.1 only and is not reachable from the internet directly.
  • • Always install with ./queryglow install for 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.