| name | shared-hosting-deployment |
| description | Shared hosting/cPanel deploy: private artifact repos, local builds, PHP/Laravel symlinks, rollback. Use when deploying to shared hosting without VPS/Docker. |
Default Stance
Manual/local build + private artifact repo + server pull + symlink public artifact dir only. GitHub Actions only when explicitly requested — SSH/secrets complexity risky on shared hosting.
Preflight Checklist
Answer before deploy:
- Domain path (
~/public_html/domain.com)
- Stack (JS static / Laravel / PHP / other)
- Build output folder (
out, dist, build, public)
- Server repo path (artifact clone)
- Current symlink target —
readlink ~/public_html/domain.com
- Rollback target exists at recorded path
- Backup/rollback dir (~/backups or release dir)
.env location (server-only, never in repo)
Never Commit Secrets
.env, API keys, DB dumps, logs/cache, node_modules, vendor — add to .gitignore before first push.
JS Static Workflow
- Locally:
npm ci && npm run build, smoke test
- Copy build output (
out/dist/build) to private artifact repo
- Commit + push artifact repo
- SSH to shared hosting:
git pull in artifact clone
- Target:
~/releases/YYYYMMDD-HHMM/<out|dist|build>
- Record:
current=$(readlink ~/public_html/domain.com)
- Verify rollback target exists on disk
- Switch:
ln -sfn ~/releases/YYYYMMDD-HHMM/out ~/public_html/domain.com
- Verify:
curl -sSI https://domain.com → 200
- On failure:
ln -sfn "$current" ~/public_html/domain.com
Laravel/PHP Workflow
.env server-only, never committed
composer install --no-dev --optimize-autoloader in release
- Record:
current=$(readlink ~/public_html/domain.com)
- Verify rollback target exists on disk:
test -n "$current" && test -d "$current"
- Storage link, cache clear, run migrations with checkpoint
- Switch:
ln -sfn ~/releases/YYYYMMDD-HHMM/public ~/public_html/domain.com
- Verify:
curl -sSI https://domain.com → 200
- On failure:
ln -sfn "$current" ~/public_html/domain.com
Keep release dirs: ~/releases/YYYYMMDD-HHMM
Backup Before Switch
- Save rollback:
current=$(readlink ~/public_html/domain.com)
- If
$current empty or path does not exist → BLOCKED
- If target is real dir (not symlinked), optional snapshot:
cp -a ~/public_html/domain.com ~/backups/domain-$(date +%Y%m%d-%H%M)
- Only then switch symlink
Verification
curl -sSI https://domain.com → 200
- Asset URLs return 200 (JS/CSS via curl)
- PHP:
tail -20 ~/logs/error.log
- Route smoke test: key pages, forms, auth paths
BLOCKED
- No backup/rollback path recorded before switch
- Unknown symlink target (readlink fails or empty)
- Symlink target is release root — exposes full source, must point only at public artifact folder
- Rollback target path does not exist on disk
- Production DB migration without explicit approval
- Secrets (.env, keys) in artifact repo
- Destructive command (
rm -rf, DROP TABLE) without checkpoint