Deployment
Deployment
QoinPay Enterprise is designed to run entirely inside infrastructure you control — a corporate data centre, a private cloud tenancy, or an air-gapped enclave. This article describes the reference architecture and the operational practices that keep a production installation secure, recoverable, and correctly sized.
On-premise vs private cloud
The deployment model is identical in both cases; only the surrounding infrastructure differs.
- On-premise gives you full physical control and is the usual choice for air-gapped or strict data-residency requirements. You supply the hosts, storage, and network.
- Private cloud (a dedicated VPC on AWS, Azure, GCP, or an OpenStack tenancy) trades some physical control for elastic compute and managed database options. QoinPay runs on any provider — it has no cloud-vendor dependencies.
In neither model does QoinPay require inbound access from the internet, and the only optional outbound dependency is the license server, which can be eliminated entirely with offline activation.
Reference architecture
A standard production topology separates three tiers:
- Application tier — one or more stateless PHP-FPM nodes behind a load balancer. Because the app is stateless (sessions and cache live in the database or a shared store), you scale horizontally by adding nodes.
- Database tier — MariaDB 10.6+ or MySQL 8.0+, ideally a primary with a replica for read scaling and failover.
- Shared storage — the
storage/tree (payslips, generated documents, queued job payloads, the.qplicfile) on shared or replicated storage so every app node sees the same files. NFS, a clustered filesystem, or object storage via the storage adapter all work.
A cron worker processes the background queue. Run it on exactly one node, or on a dedicated worker node, under a process supervisor — never on every app node simultaneously against the same queue.
TLS
Terminate TLS at the load balancer or a reverse proxy in front of public/. Require TLS 1.2 or higher, redirect all HTTP to HTTPS, and enable HSTS. If app nodes sit behind a terminating proxy, set the trusted proxy configuration so QoinPay reads the real client IP and scheme from the forwarded headers; otherwise generated absolute URLs and audit-log IPs will be wrong.
For internal traffic to the database, enable TLS on the MariaDB connection where the tiers cross a trust boundary:
DB_SSL=true
DB_SSL_CA=/etc/qoinpay/certs/db-ca.pem
Hardening
- Serve only
public/from the web root. Deny web access tostorage/,config/,vendor/, and.env. - Run PHP-FPM as an unprivileged user that owns
storage/but has no write access to application code — deploy code read-only. - Set
APP_ENV=productionso verbose error output and the install wizard are disabled. - Restrict database privileges to the single application schema; do not use a root or admin DB account.
- Put the admin UI behind an allowlist or VPN where feasible; TOTP is mandatory but network-level restriction adds defence in depth.
- Rotate
APP_KEY-adjacent secrets (API keys, DB passwords) on a schedule and keep them in a secrets manager, not in the repository.
Air-gapped operation
QoinPay is fully functional with no internet access. In an air-gapped deployment:
- Activate offline using the request/response file exchange described in Licensing & Activation.
- Disable the outbound heartbeat job; it is advisory and its absence never affects operation.
- Apply upgrades by transferring release tarballs through your approved media path, then running
php bin/console migrateon the isolated host.
Nothing in the running product phones home. The signed .qplic file is verified locally on every boot.
Backups
A recoverable installation needs three things captured together:
- The database — the source of truth for all payroll, payment, and configuration data. Take consistent logical or snapshot backups on a schedule that meets your recovery point objective. For MariaDB:
mariadb-dump --single-transaction --routines --triggers qoinpay \
| gzip > /backups/qoinpay-$(date +%F).sql.gz
- The
storage/tree — generated payslips and documents that are expensive or impossible to regenerate, plus the.qpliclicense file. APP_KEYand.env— withoutAPP_KEY, encrypted columns (including TOTP secrets) cannot be decrypted, and the database backup is effectively unrecoverable.
Test-restore backups regularly. A backup you have never restored is a hypothesis, not a recovery plan.
Sizing guidance
Sizing is driven by employee and transaction counts rather than raw user counts.
Database. Budget roughly 2–4 KB per payslip line plus audit and ledger rows. A 5,000-employee organisation running monthly payroll typically stays under 20 GB in the first year and grows a few GB per year thereafter; provision storage for 5–7 years of retained history. Give MariaDB an innodb_buffer_pool_size of 60–70% of the database host's RAM. Payroll runs are write-bursty — fast storage (NVMe or provisioned IOPS) pays off during the run window.
Application. Each PHP-FPM node handles interactive traffic comfortably at 2 vCPU / 4 GB for a few hundred concurrent administrators. Payroll processing is CPU-bound on the worker node during a run; size the worker for the peak, not the average — a run that must complete inside a payroll window benefits from more worker CPU and a higher queue:work concurrency.
Storage. Estimate storage/ at 50–150 KB per generated payslip PDF, times employees times pay periods retained. Retain per your statutory record-keeping obligations.
Start from these figures, measure your first full payroll cycle, and adjust. Continue with the API Overview to integrate QoinPay with surrounding HR and finance systems.