Manual installation
Install QueryProxy without Docker — requirements, setup steps, and the queue worker that approved queries depend on.
Updated:
Prefer running QueryProxy directly on a server? It is a standard Laravel application.
Requirements
- PHP ≥ 8.3 with the PDO drivers for the databases you will proxy
(
pdo_pgsql,pdo_mysql,pdo_sqlite;pdo_sqlsrvfor SQL Server targets) - Composer
- Node.js 20.19+ or 22.12+ (only to build the frontend assets; this is what the pinned Vite 8 needs)
Steps
git clone https://github.com/QueryProxy/QueryProxy.git
cd QueryProxy
composer install
cp .env.example .env
php artisan key:generate
touch database/database.sqlite
php artisan migrate # add --seed for the demo team
php artisan queryproxy:create-admin # the first administrator (skip it if you seeded the demo team)
npm install && npm run build
Then run the three processes:
php artisan serve # the web portal (http://localhost:7432)
php artisan queue:work --queue=queries,default --tries=1 --timeout=310 # the worker; --timeout = QUERYPROXY_EXECUTION_TIMEOUT + 10 (300 by default)
php artisan schedule:work # the scheduler
The queue worker is not optional. Approved queries execute on the worker, never inside a web request. Without a running worker, requests will be approved and then sit in the queue forever. In production, keep it alive with a process supervisor (systemd, Supervisor) and set its
--timeoutslightly aboveQUERYPROXY_EXECUTION_TIMEOUT.The
retry_afterwarning (at or below the execution timeout) is logged whenqueue:workstarts, and also forqueue:work --onceandqueue:listen. If you run the worker from the repository’s Compose example, its--timeoutcomes from the image entrypoint (QUERYPROXY_EXECUTION_TIMEOUT+ 10); on a manual install you set the number yourself.
Production notes
- Serve the app through PHP-FPM behind a real web server (nginx, Caddy) with
TLS;
php artisan serveis fine for evaluation only. The official Docker image already runs nginx + php-fpm as a non-root user — mirror that for a manual install. When behind a TLS-terminating proxy, 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 thenX-Forwarded-Protois ignored (HTTPS is not detected, no HSTS header) and audit entries record the proxy’s address instead of the client’s. Never use*. SetAPP_URLto the address users actually reach — the request host is pinned to it. See Configuration. - Keep
QUERYPROXY_RESULT_DISKon a private disk. QueryProxy refuses to write results to a publicly visible disk; result paths are predictable, so such a disk would bypass the download authorization entirely. - Set
APP_KEYexplicitly and keep it stable across deploys. It encrypts your stored connection credentials and chat secrets; a regenerated key makes them unreadable. Rotate keys viaAPP_PREVIOUS_KEYSso existing ciphertext still decrypts. - Point
DB_*at MySQL or PostgreSQL when SQLite no longer fits — see Configuration. - The scheduler powers result-retention pruning; run it via
schedule:workor a cron entry callingphp artisan schedule:runevery minute. - Cache the configuration on deploy:
php artisan config:cache && php artisan route:cache.TRUSTED_PROXIESandTRUSTED_HOSTSare read through the config, so.envis enough — re-runconfig:cacheafter changing them.