Production hardening
A checklist for running QueryProxy on a reachable network, from the application key and HTTPS to read-only connections and what the defaults already cover.
Updated:
QueryProxy stores the credentials to every database it fronts, so treat the instance itself as sensitive. Work through this list before exposing it to a network.
Recommended production setup
- For teams or users who only read, register a separate connection with a
database user that has
SELECTprivileges only. Keep write access on a different connection, since approved writes run through the connection they were submitted on. - Point that read-only connection at a replica where you can.
- Restrict the network path between QueryProxy and the target database.
- On PostgreSQL, check the transport yourself: the connection uses
sslmode=prefertoday, and a TLS setting arrives in v0.3.0.
The SQL guard is not a last line of defence. Until read-only execution on the database side arrives (v0.3.0), build the defence with database privileges as well.
Checklist
- Set
APP_KEYexplicitly and keep it stable. It encrypts connection credentials and chat secrets; changing it makes every stored credential unreadable. Generate one withdocker run --rm queryproxy/queryproxy php artisan key:generate --showand pass it as-e APP_KEY=...(every container of the same instance must share it). When rotating, move the old key intoAPP_PREVIOUS_KEYSso existing ciphertext still decrypts. Without an explicitAPP_KEY, the container generates one on first boot and persists it to the data volume. - Serve over HTTPS behind a TLS-terminating reverse proxy (the container
serves plain HTTP on
:7432). Then setSESSION_SECURE_COOKIE=true. - Set
TRUSTED_PROXIESto your proxy’s IP or CIDR whenever a proxy sits in front of QueryProxy. No proxy is trusted by default, so until you set it the app reads the connecting peer as the client:X-Forwarded-Protois ignored (HTTPS is not detected, so no HSTS header) and audit entries record the proxy’s address. Never set it to*: that lets any client forgeX-Forwarded-For, which both fakes the audit trail’s client IP and hands out unlimited login and 2FA attempts, since those throttles are keyed per IP. See Configuration. - Keep demo seeding off (
QUERYPROXY_SEED_DEMO=false, the default) on any reachable instance. - Require 2FA for privileged roles with
QUERYPROXY_REQUIRE_2FA=admins(ordba/all). Users are funneled to enrollment on next login. - Restrict connection targets with
QUERYPROXY_CONNECTION_HOST_DENYLIST/QUERYPROXY_SQLITE_ALLOWED_DIRif DBAs should not reach arbitrary hosts.
What is on by default
No configuration is needed for these:
- Security response headers, including a Content-Security-Policy, and a rate limit on the Livewire endpoint.
- Encrypted connection credentials at rest and TOTP two-factor authentication (optional until you require it).
- Comment-normalized SQL guards with an executor-enforced row ceiling.
- Replay-protected, identity-bound chat callbacks, and self-approval prevention.
- Immutable audit logs and login/webhook rate limiting.
- SSRF-guarded outbound webhooks.
- Masked-at-write, fail-closed result storage.
Next steps
- All environment variables: Configuration.
- Non-Docker installs: Manual installation.
- Chat callbacks: Slack integration and Teams integration.