K KodePressdocumentation
Laravel 11 · MySQL 8 Specification Built 2026-10-10

14 — Installation and deployment#

14.1 Requirements#

Minimum Recommended
PHP 8.3 8.3
Extensions mbstring, openssl, pdo_mysql, tokenizer, xml, ctype, json, fileinfo, zip, gd (with WebP) plus imagick, opcache
MySQL 8.0 8.0
Disk 500 MB + media 2 GB
Memory limit 128 MB 256 MB
Access SSH, one cron entry, the ability to point the document root at public/ or create a symlink same
Local only Node 20 + npm (to build assets) same

MariaDB is not a supported target. The schema uses MySQL 8 JSON functions and utf8mb4_0900_ai_ci; MariaDB would work with changes we are not maintaining.

14.2 Local setup#

git clone <repo> kodepress && cd kodepress
composer install
cp .env.example .env
php artisan key:generate

# create the database, then set DB_* in .env
php artisan kodepress:install          # migrations, seeds, first admin, storage:link

npm install && npm run build           # assets; commit public/build when it changes
php artisan serve                      # http://127.0.0.1:8000  |  /admin

.env essentials:

APP_NAME=KodePress
APP_ENV=local
APP_DEBUG=true
APP_URL=http://127.0.0.1:8000
APP_TIMEZONE=UTC                 # storage is UTC; display uses sites.timezone

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=kodepress
DB_USERNAME=root
DB_PASSWORD=

CACHE_STORE=file
SESSION_DRIVER=database
QUEUE_CONNECTION=sync
FILESYSTEM_DISK=public

KODEPRESS_ADMIN_PATH=admin
KODEPRESS_CACHE_ENABLED=true
KODEPRESS_MAX_UPLOAD_MB=8

14.3 php artisan kodepress:install#

Idempotent and safe to re-run; it detects an existing install and offers --force to reseed demo content only.

1  check requirements (PHP, extensions, writable paths) and stop with a readable list if any fail
2  detect the image driver (imagick | gd) and write it to the config cache
3  php artisan migrate --force
4  seed: roles + permissions, theme tokens (default preset), templates,
        default header + footer, main menu, demo pages/posts/categories/tags/form/media
5  prompt: first Admin name, email, password (hidden, confirmed, strength-checked)
         --name= --email= --password= for non-interactive use
6  php artisan storage:link   (or copy the symlink fallback where symlinks are disabled)
7  compile the theme CSS
8  build the block manifest
9  warm the homepage
10 print:  Done. Open https://<host>/admin   (and the admin path if it is not the default)

Non-interactive form used by CI and by the deploy script:

php artisan kodepress:install --name="Owner" --email=owner@example.com --password="..." --no-demo

14.4 cPanel deployment — first time#

The live directory is a git checkout. Production is never hand-edited and never rsynced.

# 1. local: build assets and push
npm run build
git add public/build && git commit -m "build: assets" && git push origin main

# 2. server: clone beside the webroot
ssh -p 2222 user@host
cd ~
git clone https://github.com/<org>/kodepress.git kodepress
cd kodepress
composer install --no-dev --optimize-autoloader

# 3. env
cp .env.example .env && nano .env         # APP_ENV=production, APP_DEBUG=false, DB_*, APP_URL
php artisan key:generate

# 4. database: create it in cPanel, then
php artisan kodepress:install --name="Owner" --email=owner@site.com --no-demo

# 5. document root
#    Preferred: point the domain's document root at ~/kodepress/public in cPanel.
#    If the host forbids it:
rm -rf ~/public_html && ln -s ~/kodepress/public ~/public_html

# 6. cron (cPanel -> Cron Jobs -> every minute)
* * * * * cd ~/kodepress && /usr/local/bin/php artisan schedule:run >> /dev/null 2>&1

# 7. verify
php artisan kodepress:doctor

Notes for this class of host:

14.5 Deploy script#

bin/deploy.sh, invoked over SSH. Deploy = git pull. Nothing else is a deploy.

#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
PHP=${PHP_BIN:-php}

git fetch --all --prune
git reset --hard "origin/${BRANCH:-main}"       # refuse local edits by design

composer install --no-dev --optimize-autoloader --no-interaction

$PHP artisan down --render="errors::503" || true
$PHP artisan migrate --force
$PHP artisan kodepress:blocks-refresh
$PHP artisan kodepress:theme-compile
$PHP artisan cache:clear
$PHP artisan kodepress:cache-clear            # the HTML cache
$PHP artisan route:cache
$PHP artisan view:cache
$PHP artisan up

$PHP artisan kodepress:cache-warm --limit=20
$PHP artisan kodepress:doctor

echo "HEAD: $(git rev-parse --short HEAD)  origin: $(git rev-parse --short origin/${BRANCH:-main})"

Rules this encodes:

14.6 Post-deploy verification#

Every deploy, in this order:

  1. kodepress:doctor — all green.
  2. Load the homepage as a guest; confirm a 200 and the expected header/footer.
  3. Load one inner page and one blog post; confirm the cache header reports a hit on the second load.
  4. Log in to /admin; open the page editor on a real page; confirm the preview renders.
  5. Publish a trivial change on a scratch page and confirm it appears publicly within one reload.
  6. Submit the contact form once and confirm the submission and the notification email.
  7. Confirm HEAD matches origin.

Steps 2–6 are the Playwright smoke suite (15-testing-and-qa.md), so this list is a command, not a ritual.

14.7 Rollback#

cd ~/kodepress
git log --oneline -n 10
git reset --hard <previous-sha>
composer install --no-dev -o
php artisan migrate --force          # forward-only; see below
php artisan cache:clear && php artisan kodepress:cache-clear && php artisan kodepress:cache-warm

Schema changes are forward-compatible by rule: a migration adds columns and tables, never renames or drops in the same release that starts using them. A column is dropped only one release after nothing reads it. That makes a code rollback safe without a database rollback, which is the only kind of rollback that works at 2 a.m. on shared hosting.

14.8 Backups#

14.9 Domains, TLS and email#

14.10 Environment matrix#

Local Staging Production
APP_ENV local staging production
APP_DEBUG true true false
HTML cache off (KODEPRESS_CACHE_ENABLED=false) on on
Mail log driver real, to a test inbox real
Demo content yes yes no (--no-demo)
robots.txt n/a Disallow: / real
Basic auth no yes (host-level) no
Backups no weekly nightly

Staging is optional but strongly advised for anything touching migrations or the header/footer resolver.

14.11 Troubleshooting#

Symptom Likely cause Fix
500 on every page after deploy stale cached config or views php artisan cache:clear && view:clear && route:clear; never config:cache
Styles missing public/build not committed, or the symlink missing rebuild locally and commit; storage:link
Images 404 public/storage symlink missing or host disallows symlinks storage:link, else the copy fallback
Scheduled posts never publish cron entry missing or wrong PHP binary check the Dashboard cron tick; use the host's php83 path
"I published but nothing changed" HTML cache not cleared (a bug) or a CDN in front kodepress:cache-clear; check the X-KodePress-Cache header
Editor saves then reverts two tabs open on the same page the conflict modal explains it; close one
Bengali shows as boxes font files not deployed confirm public/storage/fonts, rebuild
Upload fails over ~2 MB host upload_max_filesize the media screen shows the server limit; ask the host
Form mail not arriving sender mailbox does not exist, SPF fails fix the mailbox, re-run doctor
Permission denied writing cache wrong owner on storage/ after an SSH-as-root action chown -R <cpaneluser>:<cpaneluser> storage bootstrap/cache

KodePress documentation · generated from the Markdown sources by tools/build-docs-site.py · internal preview, not indexed.