Manual Node.js Deployment
Deploy directly on bare metal without Docker. Only use this if Docker is truly not an option.
This deployment method is not covered by the guides that ship with QueryGlow and receives limited support. You are responsible for configuring nginx, SSL certificates, Basic Auth, process management, backups and security hardening yourself.
We strongly recommend Docker deployment, which handles all of this and updates with one command.
- • Reverse Proxy: You MUST set up nginx or Caddy in front of QueryGlow
- • SSL/HTTPS: You MUST configure Let's Encrypt or similar
- • Authentication: You MUST configure Basic Auth or another auth layer
- • Firewall: Port 3000 should NOT be exposed to the internet
Without these, your database credentials and data are exposed to anyone who finds your server.
1. Install Dependencies
Requires Node.js 22 or newer. Build tools are needed for the native SQLite driver (better-sqlite3).
# Install Node.js 22 and build tools (Ubuntu/Debian)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash
sudo apt-get install -y nodejs git build-essential python3
# Verify installation
node --version # Should be v22.x.x or newer
npm --version2. Clone & Build
# Clone the repository
git clone [email protected]:mxfschr/queryglow.git
cd queryglow
# Install dependencies
npm ci
# Build for production
npm run buildWhy 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)
3. Configure Environment
Create a .env file in the checkout. The app reads it when it starts.
# Create .env file
cat > .env << 'EOF'
# REQUIRED: encryption key for stored credentials (filled in below)
SESSION_SECRET=
# Salt for the key derivation (filled in below)
ENCRYPTION_SALT=
NODE_ENV=production
# Data storage location
DATA_DIR=/home/youruser/queryglow/data
# Safe Mode (false = Safe Mode on)
# Set to true to also allow DROP TABLE, TRUNCATE, etc. in the Query Editor
QUERYGLOW_ALLOW_DESTRUCTIVE=false
# Optional AI keys: a key here wins over one entered in the app,
# so add these lines only with real keys
# OPENAI_API_KEY=
# ANTHROPIC_API_KEY=
# GOOGLE_API_KEY=
EOF
# Generate the secrets
sed -i "s/^SESSION_SECRET=.*/SESSION_SECRET=$(openssl rand -hex 32)/" .env
sed -i "s/^ENCRYPTION_SALT=.*/ENCRYPTION_SALT=$(openssl rand -hex 16)/" .env
chmod 600 .env
# Create data directory
mkdir -p /home/youruser/queryglow/data4. Run with PM2
PM2 keeps the process alive, handles logs, and restarts on crashes. Start next directly and bind it to 127.0.0.1, the way the Docker image does: plain npm start listens on every network interface.
# Install PM2 globally
sudo npm install -g pm2
# Describe the app for PM2: next on 127.0.0.1, port 3000
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [{
name: 'queryglow',
script: 'node_modules/next/dist/bin/next',
args: 'start -H 127.0.0.1 -p 3000',
}],
}
EOF
# Start QueryGlow with PM2
pm2 start ecosystem.config.js
# Save process list for auto-restart on reboot
pm2 save
pm2 startup # Follow the instructions it prints
# View logs
pm2 logs queryglowThe app is now available at http://127.0.0.1:3000 on this server only. Do NOT expose it directly to the internet.
5. Configure nginx, SSL and Auth (Required)
You must set up a reverse proxy with authentication. Here's a minimal example:
# Install nginx and certbot
sudo apt install nginx certbot python3-certbot-nginx apache2-utils
# Create htpasswd file for Basic Auth
sudo htpasswd -c /etc/nginx/queryglow.htpasswd admin
# Create nginx config
sudo nano /etc/nginx/sites-available/queryglow-c creates a new file and discards an existing one. To add a second user or reset a password later, run the same command without -c.
Add this configuration:
server {
server_name db.yourcompany.com;
auth_basic "QueryGlow";
auth_basic_user_file /etc/nginx/queryglow.htpasswd;
# Agent Access (MCP): agents use their own Bearer token
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 monitors: status only
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;
}
# Browser icons, so Safari on iOS does not ask for the password twice
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;
}
location ~ ^/apple-touch-icon(-[0-9]+x[0-9]+)?(-precomposed)?\.png$ {
auth_basic off;
return 204;
}
location / {
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;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
listen 80;
}QueryGlow uses no WebSockets, so the config sends no upgrade header. If you add a Content Security Policy, allow https://cdn.jsdelivr.net: the browser loads the SQL editor from there.
# Enable site and get SSL certificate
sudo ln -s /etc/nginx/sites-available/queryglow /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d db.yourcompany.comHow to Update
The queryglow command (update with backup and automatic rollback) needs Docker and does not apply to this setup. Back up DATA_DIR and .env yourself first, then:
cd queryglow
git pull --ff-only # or: git fetch --tags && git checkout v1.3.0
npm ci
npm run build
pm2 restart queryglowEnvironment Variables Reference
| Variable | Required | Description |
|---|---|---|
| SESSION_SECRET | Required | Encryption key (AES-256-GCM) for stored database passwords, SSH keys and AI keys entered in the app. At least 32 characters; openssl rand -hex 32 gives 64. |
| ENCRYPTION_SALT | Recommended | Salt for the key derivation. Without it the built-in default salt is used. Never change it once connections are saved. |
| NODE_ENV | Required | Set to production |
| QUERYGLOW_ALLOW_DESTRUCTIVE | Optional | false (default) = Safe Mode: blocks DROP TABLE, TRUNCATE, DELETE/UPDATE without WHERE in the Query Editor.true = Power Mode: also allows those. DROP DATABASE and statements that run commands or touch server files stay blocked. |
| DATA_DIR | Optional | Path for connections, history, saved queries and the other data files. Default: ./data in the working directory (/app/data in the Docker image). |
| PORT | Optional | Server port. Default: 3000. In this setup the port is set with -p in the PM2 args. |
| OPENAI_API_KEY | Optional | For Ask AI (OpenAI). Wins over a key entered in the app. |
| ANTHROPIC_API_KEY | Optional | For Ask AI (Claude). Wins over a key entered in the app. |
| GOOGLE_API_KEY | Optional | For Ask AI (Gemini). Wins over a key entered in the app. |
All variables with their defaults: Environment Variables.
SQLite Security Note
For security, SQLite database paths are restricted to the DATA_DIR directory. If you want to use SQLite databases, place them in your configured data directory (e.g., /home/youruser/queryglow/data/).