What the setup script does
SixPreflight tells you what to change. The setup script is the other half of the same folder — it makes the change for you. Where the dashboard asks you to copy a value and paste it yourself, setup-6ammart.php applies the same recommendation directly, and asks first about anything that trades one property for another.
What it is
A bash script, despite the .php extension in its name. That extension is deliberate — it stops a web server from ever handing the file out if someone requests it by URL. PHP takes the file, hits an exit statement on line 3, and returns nothing. Run it with bash, never through a browser.
It is the fallback route behind SixPreflight, not the first one. The dashboard still asks you to make each change by hand first, because a person who makes a change understands what they changed. This script exists for when that is not practical — it applies everything it can apply safely, and it asks about anything where a real tradeoff exists.
Every recommendation comes from the same tuning catalog mentioned on The "What to fix" page — around 84 entries across seven layers.
Each one carries a flag saying whether it is safe to apply without asking. About 40 are. The rest are either prompted with the tradeoff stated in one line, or printed as an instruction for you to follow yourself.
No entry in that second group is ever applied silently, not even when you tell the script to skip every other prompt.
The four modes
The default writes nothing. Running the script by accident cannot change anything on your server.
| Mode | What it does |
|---|---|
--dry-run (default) | Prints every change it would make. Writes nothing — this is what you get with no arguments at all. |
--check | Reports current value against recommended value, as a diff. Writes nothing. |
--apply | Does the work. Asks before anything risky. |
--apply --yes | Accepts every safe-to-automate item without asking. Risky items still ask, every time. |
See what it would do — writes nothing:
bash preflight/bin/setup-6ammart.phpCurrent vs recommended, as a report:
bash preflight/bin/setup-6ammart.php --checkDo the work, asking where a tradeoff exists:
bash preflight/bin/setup-6ammart.php --applyOne layer only:
bash preflight/bin/setup-6ammart.php --apply --only laravel --only 6ammartEverything except the database:
bash preflight/bin/setup-6ammart.php --apply --skip mysqlIt must run as root, since it edits php.ini, my.cnf, sysctls and systemd units. Anything that touches the application itself — running artisan or composer — is dropped to the web user instead, because a cache file written by root is one the web user can no longer rebuild later.
The three safety rules
- Every write is gated behind one function that returns false in dry-run and check modes, before any file is even opened
- Every file is backed up before it is edited, preserving mode and owner, so a run can always be undone
- Application commands like artisan and composer never run as root — they run as the web user
The seven layers
They run in a fixed order — a layer that cannot break the site runs before one that can.
| Layer | What it covers |
|---|---|
php | OPcache, JIT, memory and execution limits, upload sizes, display_errors, disable_functions. |
php-fpm | Pool sizing — process manager mode and worker count, measured against cores and real memory per worker. |
os | Swap presence, file-descriptor limits, vm.overcommit_memory. Live and reversible in one command. |
mysql | InnoDB buffer pool and log file size, sql_mode, character set, binary logging. |
nginx | Deny blocks for files above the web folder, static-expires rules, upload limits, gzip, TLS versions. |
laravel | Route cache, storage ownership, .env file mode, APP_DEBUG, and the queue worker. |
6ammart | Application settings — the scheduler cron, push notification settings, ghost modules, timezone, mail config, the websocket server. |
One nginx safeguard worth knowing about
6amMart's own deny-list for files above the web root missed several real paths: both installer SQL dumps, a leftover zip archive, internal markdown files, and a bootstrap/app.php that was being executed directly by the web server and disclosing an absolute path in the error it returned.
So the script probes each path by request rather than trusting the deny-list on paper. It adds a block only for the ones that actually answer, then re-fetches the same paths afterward to confirm the block wins.
That last step is the point: a rule can be present and valid in the config and still lose to another rule that matches the same path first.
The document root itself is not changed. 6amMart runs correctly whether the vhost is rooted at the project folder or at public/, so restructuring a working vhost is not something this script does — only the deny blocks that decide which files a request can actually fetch.
The queue worker and scheduler
This is where the script earns its keep, because getting either of these wrong is completely silent — nothing errors, nothing logs, and the shop keeps looking healthy.
The queue worker. QUEUE_CONNECTION and a running worker are really one setting split across two places. With a queue configured and no worker, jobs pile into a table and nothing ever drains them — no push notification, no order mail. The script checks the pair together rather than each half on its own, writes a supervised systemd unit if none exists, and — if jobs are already stuck in the table — counts them and warns you before starting the worker, because hours of stale notifications arriving all at once is worse than none arriving.
The scheduler cron. Store and rider disbursements, subscription reminders and the monthly order reminder all run from Laravel's scheduler. Without the crontab line, none of them ever happen, and nothing is written anywhere to say why. The script adds the line to the web user's crontab specifically — never root's — because artisan run as root leaves root-owned files that the web user can no longer rebuild.
What it will never do
These are deliberate refusals, not gaps — each one is a case where the obvious automatic fix leaves a site that keeps serving pages while something quietly breaks.
| It will not | Why |
|---|---|
Run config:cache | This codebase reads some keys at runtime that would go null once the config is cached — caching or clearing it can break a working install either way. |
Run key:generate | Rotating APP_KEY makes every encrypted column and every session permanently unreadable. |
Run composer install | A production reinstall can remove a package a module needs at runtime. |
| Flush failed jobs | The payload of a failed job is the only record of what broke. |
Touch public/storage | 6amMart does not use the symlink a storage-link command would create. |
| Restart the database | Brief downtime — deserves its own decision, not an automatic one. |
| Overwrite a unit file it did not write | An existing file may be someone's deliberate configuration. |
Undoing a run
Every --apply run writes a restore script and prints its path at the end. It restores the files that run changed, from the backups that run took.
bash /root/preflight-restore-20260807-101530.shIt restores files only — a live database setting changed with SET GLOBAL, a systemd unit that was created, or a crontab line that was added are not files, and are listed individually in the run's own output instead.
Reading the summary
| Count | Meaning |
|---|---|
| ok | Already at the recommended value. Nothing was done. |
| applied | Changed by this run — or, in dry-run, what would change. |
| manual | Deliberately left to a person, with the instruction printed. Not a failure. |
| failed | Attempted and did not verify. The script exits with an error if this is above zero. |
| skip | Not applicable to this box, or filtered out by --only / --skip. |
Run --check again afterward. The honest verification of a run is a fresh read of the server, not the run's own report of what it did.