cipi php

PHP 8.5 is pre-installed during setup. Additional versions (7.4–8.4) can be added at any time from the ondrej/php PPA (the de-facto standard for PHP on Ubuntu — new releases typically land there within days of their official release).

bash
$ cipi php list              # list installed versions, status, and system default
$ cipi php install 8.4       # install an additional PHP version
$ cipi php switch 8.4        # set system default (root/cipi, API pool)
$ cipi php remove 8.4        # remove a version (blocks if system default or apps use it)

System default vs app-specific PHP

The system default is the PHP version used by the root and cipi users, the Cipi API FPM pool, and the API queue worker. Use cipi php switch <ver> to change it. The command migrates the API pool, restarts the API worker, and sends an email notification if SMTP is configured.

Each app has its own PHP version (set at app create or via cipi app edit --php=). Deployer, Composer, crontab deploy triggers, and cipi sync import always run with the app's configured PHP — never the system default.

To switch an existing app to a different version:

bash
$ cipi app edit myapp --php=8.5

This hot-swaps the PHP version with zero downtime: updates the FPM pool, Nginx socket, Supervisor workers, crontab, Deployer config, and .env in one atomic operation.

cipi db

Cipi creates a dedicated MariaDB database for each Laravel app automatically during app create. Custom apps do not get a database — use cipi db create --name=<app> if you need one (e.g. for WordPress or another CMS). After database setup, a ready-to-use mariadb+ssh:// connection URL is displayed, combining SSH credentials, server IP, and database information in a single copyable string for GUI clients like TablePlus, DBeaver, or Sequel Pro. You can also manage additional standalone databases.

bash
$ cipi db create                     # interactive
$ cipi db create --name=analytics    # non-interactive
$ cipi db list                       # list all databases with sizes
$ cipi db backup myapp               # dump to /var/log/cipi/backups/
$ cipi db restore myapp backup.sql.gz
$ cipi db password myapp             # regenerate database password
$ cipi db delete analytics

cipi alias

Add multiple domains or subdomains to any app. After adding aliases, run cipi ssl install to provision or renew the certificate with SAN coverage for all domains.

bash
$ cipi alias add myapp www.myapp.com
$ cipi alias add myapp myapp.it
$ cipi alias list myapp
$ cipi alias remove myapp myapp.it

cipi ssl

Certbot manages Let's Encrypt certificates. Certificates auto-renew via a weekly cron. cipi ssl status shows expiry dates with color-coded warnings: green (>30 days), yellow (14–30 days), red (<14 days).

bash
$ cipi ssl install myapp   # provision / renew — includes all aliases (SAN)
$ cipi ssl renew            # force renewal of all certificates
$ cipi ssl status           # show all certs with expiry dates
After adding domain aliases with cipi alias add, always run cipi ssl install again to provision a new SAN certificate covering all domains.

cipi backup

Back up databases and storage to Amazon S3 or any S3-compatible provider (Hetzner Object Storage, DigitalOcean Spaces, Backblaze B2, MinIO, etc.).

Setup

bash
$ cipi backup configure
# → AWS Access Key ID
# → AWS Secret Access Key
# → Bucket name
# → Region
# → Endpoint URL (leave empty for AWS; required for other providers)

S3-compatible endpoints

Provider Endpoint URL
AWS S3 leave empty
Hetzner https://<datacenter>.your-objectstorage.com
DigitalOcean Spaces https://<region>.digitaloceanspaces.com
Backblaze B2 https://s3.<region>.backblazeb2.com
MinIO https://your-minio-host

Running backups

bash
$ cipi backup configure              # configure S3 credentials
$ cipi backup run                    # backup all apps
$ cipi backup run myapp              # backup a single app
$ cipi backup list                   # list all backups
$ cipi backup list myapp             # list backups for one app
$ cipi backup prune myapp --weeks=4  # delete backups older than 4 weeks

Each backup uploads to s3://your-bucket/cipi/appname/YYYY-MM-DD_HHMMSS/ and contains:

  • db.sql.gz — compressed database dump
  • shared.tar.gz — the entire shared/ directory (.env + storage/)

Scheduling automatic backups

bash
# Add to root crontab (crontab -e)
0 2 * * * /usr/local/bin/cipi backup run >> /var/log/cipi/backup.log 2>&1

Pruning old backups from S3

Backups accumulate over time. Use cipi backup prune to delete backup folders older than N weeks from S3. Run it as a cron job alongside the backup itself.

bash
$ cipi backup prune myapp --weeks=4   # delete backups older than 4 weeks
$ cipi backup prune myapp --weeks=2   # keep only the last 2 weeks

Add both commands to the root crontab to run automatically:

bash
# root crontab — backup at 02:00, prune at 03:00 (keep 4 weeks)
0 2 * * * /usr/local/bin/cipi backup run myapp >> /var/log/cipi/backup.log 2>&1
0 3 * * * /usr/local/bin/cipi backup prune myapp --weeks=4 >> /var/log/cipi/backup-prune.log 2>&1
cipi backup prune reads S3 credentials from /etc/cipi/backup.conf, written by cipi backup configure. It works with any S3-compatible provider. Adjust --weeks to match your retention policy (e.g. --weeks=2 for two weeks, --weeks=8 for two months).

User crontab

Cipi automatically adds a crontab entry for the Laravel scheduler when an app is created:

bash
# installed automatically by cipi app create
* * * * * /usr/bin/php8.5 /home/myapp/current/artisan schedule:run >> /dev/null 2>&1

This entry runs as the myapp Linux user every minute, using the PHP version selected for the app. It is updated automatically when you change PHP version via cipi app edit myapp --php=X.

Viewing the current crontab

bash
# as root — view the app user's crontab
$ crontab -u myapp -l

# or after switching to the app user
$ su - myapp
myapp@server:~$ crontab -l

Adding custom cron jobs

You can add extra cron jobs to the app user's crontab. Switch to the app user first to ensure jobs run with the correct user context and file permissions:

bash
$ su - myapp
myapp@server:~$ crontab -e

Example entries you might add:

bash
# existing Laravel scheduler (do not remove)
* * * * * /usr/bin/php8.5 /home/myapp/current/artisan schedule:run >> /dev/null 2>&1

# nightly database backup at 2 AM
0 2 * * * /usr/local/bin/cipi db backup myapp >> /home/myapp/logs/backup.log 2>&1

# custom script every 15 minutes
*/15 * * * * /home/myapp/current/scripts/sync.sh >> /home/myapp/logs/sync.log 2>&1
Do not remove the Laravel scheduler entry. Cipi does not re-add it automatically if deleted — you would need to run cipi app edit myapp --php=<current-version> to restore it. Always keep the schedule:run line as the first entry so it is easy to identify.
Cron jobs run as the app user. They respect the same filesystem restrictions as the app itself. If a cron job needs to write files, make sure the target path is inside /home/myapp/. Jobs that require root access should be added to the root crontab instead, with crontab -e as root.

Checking if cron is working

bash
# check system cron log
$ grep CRON /var/log/syslog | grep myapp | tail -20

# check Laravel scheduler execution
$ cipi app artisan myapp schedule:list

cipi worker

Every app gets a default Supervisor worker for the default queue. You can add additional queues with custom process counts and timeouts.

bash
$ cipi worker add myapp --queue=emails --processes=3
$ cipi worker add myapp --queue=exports --processes=1 --timeout=7200
$ cipi worker list myapp
$ cipi worker edit myapp --queue=default --processes=3
$ cipi worker remove myapp emails
$ cipi worker restart myapp   # restart all workers for the app
$ cipi worker stop myapp      # stop all workers for the app (used during deploys)
Flag Description
--queue=<name> Queue name to consume (e.g. default, emails, exports)
--processes=<n> Number of parallel worker processes
--timeout=<seconds> Job timeout in seconds. Default is 60.

Workers are stopped before the symlink swap and restarted after every deploy, preventing Supervisor from picking up stale artisan paths. Supervisor is configured with autorestart=unexpected so workers only restart on unexpected exits, not on graceful stops.

cipi firewall

Cipi installs UFW with ports 22, 80, and 443 open by default. Use the firewall commands to manage additional rules without touching UFW directly.

bash
$ cipi firewall allow 3306                  # open a port
$ cipi firewall allow 3306 --from=10.0.0.5  # allow from specific IP
$ cipi firewall allow 3306 --from=10.0.0.0/24 # allow from subnet
$ cipi firewall deny 8080                   # block a port
$ cipi firewall list                        # show all rules

cipi ban

Inspect and manage Fail2ban bans directly from the CLI. Cipi configures Fail2ban with progressive banning: a 24-hour base ban that doubles on each repeat offence up to a 7-day cap, with max retries reduced to 3. A dedicated recidive jail bans repeat offenders for 7 days after 3 bans within 24 hours.

bash
$ cipi ban list                        # list all banned IPs, grouped by jail
$ cipi ban unban 203.0.113.42           # unban a specific IP from all jails

cipi ban list

Lists every IP currently banned by Fail2ban, grouped by jail (e.g. sshd, recidive). Useful for a quick security check or before running an unban.

cipi ban unban <IP>

Removes the given IP from all Fail2ban jails at once. Handy when a legitimate user or CI runner gets locked out by mistake.

Existing installations are upgraded automatically by the 4.3.0 migration script when you run cipi self-update. No manual configuration is needed.

cipi service

Check and control the system services that power Cipi directly from the CLI. Nginx uses a graceful reload (zero downtime) instead of a full restart.

bash
$ cipi service list                    # status of all services
$ cipi service list nginx              # status of a specific service
$ cipi service restart                 # restart all services
$ cipi service restart nginx           # graceful reload (zero downtime)
$ cipi service restart php             # restart all PHP-FPM versions
$ cipi service start fail2ban
$ cipi service stop supervisor         # asks for confirmation

Supported service names: nginx, mariadb, redis-server, supervisor, fail2ban, php<ver>-fpm (e.g. php8.5-fpm). The keyword php targets all installed PHP-FPM versions at once.

Redis is included in the default stack (from Cipi 4.0.4). It is installed with a password, bound to localhost only, and its credentials (user, password) are saved in /etc/cipi/server.json and shown at the end of installation. redis-server is added to the unattended-upgrades blacklist — Cipi manages it, so it is not auto-upgraded automatically.

cipi ssh — SSH Key Management

Manage the authorized SSH keys for the cipi user — the admin SSH entry point. The cipi user (group cipi-ssh) uses public-key only; root login is disabled. App users (group cipi-apps) connect with password — see SSH as the app user.

Commands

bash
$ cipi ssh list                 # list all authorized keys with fingerprint, comment, and current-session marker
$ cipi ssh add [key]             # add a new SSH public key (validates format, prevents duplicates)
$ cipi ssh remove [n]            # remove a key by number
$ cipi ssh rename [n] [name]     # change the display name / comment of a key

Safety mechanisms

cipi ssh remove includes two safeguards to prevent lockout:

  • Current-session protection — you cannot remove the key used by your active SSH session.
  • Last-key protection — you cannot remove the last remaining authorized key.

Key comments

SSH keys are stored with their original comments intact, making it easy to identify who each key belongs to. Use cipi ssh rename to change the display name of any key:

bash
# list keys to find the number
$ cipi ssh list

# rename key #2
$ cipi ssh rename 2 "john-macbook"

Email notifications

When SMTP is configured, Cipi sends an email alert every time a key is added, removed, or renamed. The notification includes the server hostname, IP address, key fingerprint, key comment, timestamp, and remaining key count. Rename notifications also include the old and new key name.

cipi — Server & Self-Update

Top-level commands for server status and Cipi self-management.

bash
$ cipi status              # CPU, RAM, disk, services, PHP versions, apps
$ cipi version             # show installed Cipi version
$ cipi self-update         # update Cipi to the latest version
$ cipi self-update --check # check for updates without installing

Password & credential reset

Cipi provides commands to regenerate server-level passwords. New passwords are stored in /etc/cipi/server.json (encrypted via Vault) and displayed on screen. Save them immediately — they are shown only once.

bash
$ cipi reset root-password     # regenerate the root Linux user SSH password
$ cipi reset db-password       # regenerate the MariaDB root password
$ cipi reset redis-password    # regenerate the Redis password and restart the service
cipi reset redis-password restarts the Redis service. Connected clients will be temporarily disconnected. If your apps use Redis for cache or sessions, expect a brief interruption.