App restart automation¶
App restart automation is optional. After Patcherly applies a fix on your server, the connector can run a short list of shell commands you define from a YAML manifest — for example to reload a process, clear a cache, regenerate autoload files, or run your own tests. It runs only after a successful apply, only when your plan includes it, and only on target types that allow it (Python, Node.js, PHP).
The name is "app restart" because the most common use is restarting a long-running process, but the manifest is generic — it runs whatever shell steps you put in. Many PHP setups don't need a literal restart, but still benefit from running cache clears, OPcache resets, or PHPUnit smoke tests after a patch.
Which plans include this and how limits work are listed on the Pricing page (plan names and features change over time).
When you'd use it¶
You don't have to enable this — Patcherly's basic "approve fix → file is patched → done" flow works without it. Turn it on when you want one of the following to happen automatically after every successful patch on a target:
| Use case | Typical commands |
|---|---|
| Reload a long-running process so it picks up the new code | systemctl reload myapp, pm2 reload api, supervisorctl restart worker:*, kill -HUP $(cat /run/gunicorn.pid) |
| Clear or rebuild a framework cache | php artisan config:clear, php bin/console cache:clear --env=prod, drush cache:rebuild, npm run build |
Reset PHP OPcache (production tuning with opcache.validate_timestamps=0) | systemctl reload php-fpm, or call a privileged URL that runs opcache_reset() |
| Regenerate Composer / autoload files | composer dump-autoload --optimize --no-dev |
| Run a smoke test on the patched code before declaring the fix done | pytest tests/smoke -q, npm test -- --runInBand, ./vendor/bin/phpunit --testsuite=Smoke, php artisan test --filter=SmokeTest |
| Restart a queue worker so it loads the new code | php artisan queue:restart, pm2 restart worker, supervisorctl restart laravel-horizon |
You can mix several steps in one manifest — for example, clear cache → reload php-fpm → run a 30-second PHPUnit smoke test. If any step fails, the connector reports it back and the manifest can be auto-disabled for the target until you fix it.
Who can use it¶
- Plan: Your workspace must include app restart automation for the target (the dashboard shows App restart when it is available). If you do not see it, open Profile → Billing (as the workspace owner) or check Pricing for an upgrade path.
- Target type:
python,nodejs, andphptargets support this — those connectors run as a real OS process and are allowed to shell out. WordPress targets do not — the bundled plugin lives insidewp-content/plugins/and, by WordPress.org plugin-directory rules, cannot run arbitrary shell commands. Use manual restart guidance on the error, or add a standalone Python/Node.js/PHP target on the same host if you need automation.
What you configure¶
- Open Dashboard → Targets, select a Python, Node.js, or PHP target.
- Use the App restart On/Off toggle to pause or resume automation without deleting your YAML.
- Open Configure to edit the manifest (YAML).
- Read the risk notice, then confirm that you understand commands run as the same OS user as the agent and may affect services on that machine.
- Save the manifest.
The first time you save with confirmation (or when you Save after a failure auto-disable), Patcherly turns the On toggle on for you. Later Save while the toggle is Off keeps your pause — edit the YAML freely without silently resuming restarts.
The dashboard shows a read-only preview of step names and commands after save. That preview is static (what is stored on the server), not a live view of the remote server.
You can paste YAML or upload a .yaml / .yml file.
Pause vs failure lock¶
- On/Off toggle — your intentional pause/resume. Manifest stays on the target.
- Automation disabled after a failed run — a separate safety lock. The Targets row shows an App restart warning badge and a warning-styled Configure button until you review the manifest and Save to re-enable. The modal explains the lock; saving clears the warning. That lock is not the same as turning the toggle Off. Workspace admins can also filter the Targets list to App restart review.
Manifest shape¶
version: 1
when: on_fix_success_if_restart_required # or any other non-empty value to always run on success
working_directory: /opt/myapp # optional; defaults to the agent's cwd
dry_run: false # optional; force preview-only mode for this manifest
steps:
- name: reload_app
run: systemctl reload myapp
timeout_seconds: 120
ignore_failure: false
when—on_fix_success_if_restart_required(the default) only runs the steps when the fix payload from Patcherly is flagged as needing a restart (typical for Python/Node fixes that change long-running code). Set it to any other value (e.g.on_fix_success) to run the steps after every successful patch — useful for PHP cache clears or running tests, where the AI rarely setsrestart_required.steps— each step has arunshell string (or an array of argv tokens), an optionaltimeout_seconds(default 120), and an optionalignore_failure: trueso a failed step does not disable automation.working_directory— directory to run each step from. Defaults to the agent's working directory.dry_runat the manifest level forces preview-only mode regardless of the env var below.- Secrets — do not put API keys or passwords in the YAML. Use environment variables already configured on the server.
Examples per language¶
The same YAML format applies to all three connectors. Adapt the run: lines to your stack.
Python (Gunicorn + Django, Pro):
when: on_fix_success_if_restart_required
working_directory: /srv/myapp
steps:
- name: reload_gunicorn
run: systemctl reload gunicorn-myapp
timeout_seconds: 30
- name: smoke_test
run: ./venv/bin/pytest tests/smoke -q
timeout_seconds: 60
ignore_failure: false
Node.js (PM2 + Express):
when: on_fix_success_if_restart_required
working_directory: /var/www/api
steps:
- name: reload_pm2
run: pm2 reload api --update-env
timeout_seconds: 30
- name: smoke_test
run: npm test -- --runInBand --testPathPattern=smoke
timeout_seconds: 90
ignore_failure: false
PHP (Laravel on PHP-FPM):
when: on_fix_success # PHP often doesn't need a restart, so we run on every successful patch
working_directory: /var/www/myapp
steps:
- name: clear_caches
run: php artisan config:clear
timeout_seconds: 30
- name: reload_php_fpm # only needed if you run with opcache.validate_timestamps=0
run: systemctl reload php-fpm
timeout_seconds: 30
ignore_failure: true
- name: phpunit_smoke
run: ./vendor/bin/phpunit --testsuite=Smoke
timeout_seconds: 120
ignore_failure: false
PHP (Symfony):
when: on_fix_success
working_directory: /var/www/symfony-app
steps:
- name: clear_cache
run: php bin/console cache:clear --env=prod --no-warmup
timeout_seconds: 60
- name: warmup_cache
run: php bin/console cache:warmup --env=prod
timeout_seconds: 60
- name: phpunit_smoke
run: ./bin/phpunit --testsuite=smoke
timeout_seconds: 120
ignore_failure: false
PHP (queue worker — restart Horizon after a patch):
when: on_fix_success
working_directory: /var/www/myapp
steps:
- name: restart_horizon_workers
run: php artisan horizon:terminate
timeout_seconds: 15
ignore_failure: true
- name: clear_route_cache
run: php artisan route:clear
timeout_seconds: 15
Safety and limits¶
- Tamper detection: Patcherly stores a fingerprint of your manifest, and the connector checks that the manifest still matches before running anything. If the YAML on its way to your server has been altered, the connector refuses to run any of the steps (
content_sha256_mismatchis reported as a failed run, not a skip). - String-form vs array-form commands: Each
runstring is tokenised into argv and checked for shell metacharacters (&&,||,|,;, backticks,$(,>,<) before launch — use multiple steps instead of chaining. An array-formrun(YAML list, e.g.['python', 'scripts/reload.py']) is passed to the OS without that string denylist scan; argv[0] must still be on the connector binary allowlist (python,node,php, etc.). Prefer array-form when a command needs semicolons inside a-e/-cbody. - Binary allowlist: Only approved program names can be argv[0]. Defaults include language runtimes (
python,node,php, …) and common process managers (systemctl,pm2,supervisorctl). Steps still require a signed opt-in manifest, hourly caps, and the string-form denylist. - Rate limiting: Successful automated restarts are capped per target per rolling hour. On Targets, At limit (e.g.
0 / 3) appears only when that hourly cap is reached — not when automation is disabled for another reason (failed step, tamper, superadmin lock, or toggle Off). - Automation disabled: If a step fails (and the step is not marked
ignore_failure: true), automation can be disabled for that target until you fix the manifest and confirm again. You may see a needs review flag in the dashboard. That state is separate from At limit. See Known disable reasons (unsafe_command,binary_not_allowed,step_failed,step_timeout, hash/entitlement locks). - Dry run: On the agent host, set
PATCHERLY_POST_APPLY_DRY_RUN=1to log what would run without executing commands. You can also setdry_run: trueat the top of the manifest itself.
After a successful run — Error History¶
When post-apply steps run for a fix, the error’s History modal records workflow steps (under Patching Step in audit):
| History label | Meaning |
|---|---|
| App restart completed | Steps ran successfully (app_restart_ran). |
| App restart failed | A step failed or policy blocked execution (app_restart_failed). |
| App restart skipped | Connector skipped automation (rate limit, not enabled, restart not required, etc.) — reason is in the row detail (app_restart_skipped). |
These appear in History even when the error status is Fixed. A past restart failure does not re-open the top Apply dispatch failed banner after a later successful patch.
Agent testing after restart¶
If your plan includes agent testing, the connector runs the same automated tests after a fix whether or not post-apply steps ran: the order is apply patch → post-apply commands (if any) → report the result → run tests. Tests therefore see the app after any reload, not only after a file change. If your service needs a short warm-up before tests pass (for example, PHP-FPM reconnecting to a pooled DB after systemctl reload), set PATCHERLY_POST_APPLY_TEST_DELAY_SEC on the agent host (seconds). The agent-testing suite runs on the target itself — it is separate from the basic URL health check Patcherly runs from its own side (see Rollback → Basic health check).
Notifications¶
You may receive in-app or email notifications when:
- A post-apply run succeeds or is skipped (e.g. rate limit).
- Automation is disabled due to a failed step or policy.
See Notifications.
Related help¶
- Restarting apps — general restart guidance (manual and automated).
- Security: post-apply restart safety — trust boundaries and mitigations.
- Python connector · Node.js connector · PHP connector
- Local demo stacks: operators testing boom 4–6 and restart harnesses — see the connector target stacks README (not required for production hosts).
WordPress¶
The WordPress plugin does not execute post-apply manifests — it patches files only and never runs shell commands. Use the restart instructions shown in the product, or run commands yourself on the server, when your WordPress site needs a reload after a fix. If you need automation on a WordPress host, install a standalone PHP connector alongside the plugin and configure the manifest there.