Backend Deployment Guide
Everything needed to take the CargoPRO admin backend to a production server: requirements, web server configuration, PHP settings, folder permissions, and process management for the queue worker and scheduler.
Server requirements
| Component | Requirement | Notes |
|---|---|---|
| PHP | 8.2 or newer | Extensions: pdo_mysql, mbstring, openssl, tokenizer, xml, ctype, json, fileinfo, curl; optional gd + bcmath (image handling / arithmetic). The installer verifies all of these in step 2. |
| Database | MySQL 5.7+ / MariaDB 10.3+ | An empty database + a dedicated user with all privileges on it. The installer can create the database if the user has CREATE rights. |
| Web server | Nginx or Apache 2.4+ | Document root must point at the public/ folder β see Web server configuration. |
| Composer | 2.x | Only needed if you deploy from source without vendor/. |
| Node.js | 18+ (build-time only) | Only to rebuild frontend assets with npm run build; the compiled public/build ships ready-to-use. |
| PHP settings | upload_max_filesize & post_max_size ≥ 20M, max_execution_time ≥ 120 | Pre-alert and shipment files are up to 10 MB each; raise the limits if you accept larger uploads. |
| Optional | Redis, SSL certificate, cron | Redis improves queue/cache throughput at scale; SSL is effectively mandatory; cron (or supervisor) drives the scheduler. |
The Flutter apps have their own toolchain requirements β see Requirements & Stack for the full table including Flutter and Android Studio versions.
Web server configuration
The single most important rule: the document root must point at the
public/ folder, never at the project root. That keeps
.env, storage/, vendor/ and the
database files unreachable from the web.
Server block for /etc/nginx/sites-available/cargopro
(symlink it into sites-enabled):
server {
listen 80;
listen [::]:80;
server_name your-domain.com;
root /var/www/cargopro/public; # <-- the public folder
index index.php;
client_max_body_size 25M; # match your PHP upload limits
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
}
location ~ /\.(?!well-known).* {
deny all; # block dotfiles except ACME challenges
}
}Enable it and reload:
sudo ln -s /etc/nginx/sites-available/cargopro /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx
VirtualHost for
/etc/apache2/sites-available/cargopro.conf:
<VirtualHost *:80>
ServerName your-domain.com
DocumentRoot /var/www/cargopro/public # <-- the public folder
<Directory /var/www/cargopro/public>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/cargopro-error.log
CustomLog ${APACHE_LOG_DIR}/cargopro-access.log combined
</VirtualHost>The shipped public/.htaccess handles front-controller
rewriting β it only needs mod_rewrite enabled and
AllowOverride All:
sudo a2enmod rewrite sudo a2ensite cargopro sudo systemctl reload apache2
HTTPS / SSL
HTTPS is effectively mandatory β the apps talk to the API over the network, mixed content is blocked by stores, and secure cookies need it. With Certbot on a VPS:
sudo certbot --nginx -d your-domain.com # or --apache
Certbot rewrites the server block for automatic renewal. On cPanel use
SSL/TLS Status → Run AutoSSL. After enabling HTTPS, make
sure APP_URL=https://your-domain.com in .env
so generated links match.
Folder permissions
Laravel must be able to write to two locations (the installer checks
them in step 3). On a VPS where the site runs as
www-data:
sudo chown -R www-data:www-data /var/www/cargopro/storage /var/www/cargopro/bootstrap/cache
sudo find /var/www/cargopro/storage -type d -exec chmod 775 {} \;Everything else can stay owned by your deploy user and read-only for the web server.
Process management (queue worker & scheduler)
The installer sets QUEUE_CONNECTION=database. Two
background processes keep CargoPRO running:
| Process | What it drives | How |
|---|---|---|
| Queue worker | Bulk email/SMS/WhatsApp and push notification fan-out | php artisan queue:work β a long-running process managed by supervisor or systemd (below), or cron on shared hosting |
| Scheduler | Recurring maintenance tasks | php artisan schedule:run every minute via cron β on every hosting type |
Supervisor (recommended for VPS)
Supervisor keeps the queue worker alive, restarts it if it crashes,
and starts on boot. Create
/etc/supervisor/conf.d/cargopro-worker.conf:
[program:cargopro-worker] process_name=%(program_name)s_%(process_num)02d command=php /var/www/cargopro/artisan queue:work --sleep=3 --tries=3 --timeout=300 autostart=true autorestart=true stopwaitsecs=3600 user=www-data numprocs=2 redirect_stderr=true stdout_logfile=/var/www/cargopro/storage/logs/worker.log
sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start "cargopro-worker:*"
numprocs=2 runs two workers; one is plenty
for most installs. stopwaitsecs must exceed your longest
job so a deploy does not kill a notification batch mid-flight.
systemd (alternative)
If you prefer plain systemd, save as
/etc/systemd/system/cargopro-worker.service:
[Unit] Description=CargoPRO queue worker After=network.target mysql.service [Service] User=www-data Restart=always RestartSec=5 ExecStart=/usr/bin/php /var/www/cargopro/artisan queue:work --sleep=3 --tries=3 --timeout=300 [Install] WantedBy=multi-user.target
sudo systemctl enable --now cargopro-worker
Cron (shared hosting / cPanel)
Where you cannot run daemons, two cron jobs emulate both processes β add them in cPanel → Cron Jobs (adjust the path):
* * * * * cd /home/USER/public_html && php artisan schedule:run >> /dev/null 2>&1 * * * * * cd /home/USER/public_html && php artisan queue:work --stop-when-empty --max-time=50 >> /dev/null 2>&1
The second job drains the queue every minute and exits after 50 seconds, so jobs never overlap. The scheduler entry is always required regardless of hosting type.
Deploying an update
When you ship a new version of the backend to an existing server:
php artisan down # maintenance mode (optional) # ... upload/extract the new files, keep the existing .env ... composer install --no-dev --optimize-autoloader # if vendor/ is not shipped php artisan migrate --force # apply schema updates php artisan storage:link # no-op if it exists php artisan config:cache && php artisan route:cache && php artisan view:cache php artisan queue:restart # workers pick up the new code php artisan up
queue:work holds code in memory. Without
queue:restart (or supervisorctl restart) it
keeps running the previous release until it dies naturally.
Production hardening checklist
Security
- Debug off β the installer writes
APP_ENV=productionandAPP_DEBUG=false; confirm withphp artisan about. - HTTPS β SSL active and
APP_URL=https://…matching it. - Installer locked β the
storage/app/installedmarker blocks the wizard from re-running. Back it up together with.env. - Strong admin password β the finish page shows it once; change it under Profile if it was shared anywhere.
- Document root on
public/β never the project root (see Web server configuration).
Background processing
- Queue worker running β supervisor/systemd on a VPS, the cron fallback on shared hosting; verify with
php artisan queue:monitoror by sending a test notification. - Scheduler cron installed β
schedule:runevery minute. - Storage link present β
php artisan storage:link(re-run after moving the site).
Performance & data safety
- Caches primed β
config:cache,route:cache,view:cache(skipconfig:cacheon cPanel if you edit.envoften; re-run after any edit). - Backups enabled β Tools → Backups, and download one copy off-server after go-live.
- End-to-end test β register → pre-alert → package → payment → tracking from a private browser window.