cipi app create

cipi app create supports two app types: Laravel (default) and Custom (--custom). Laravel apps get a fully isolated environment: Linux user, PHP-FPM pool or Laravel Octane (FrankenPHP) since v5.0, Nginx vhost, database (MariaDB by default; optional PostgreSQL since v4.8.0), Supervisor worker, crontab entry, Deployer zero-downtime releases, SSH deploy key, and auto-compiled .env. Custom apps are simpler — see custom apps for details.

Laravel app (default — PHP-FPM)

bash
$ cipi app create

Laravel Octane (FrankenPHP)

bash
$ cipi app create --octane
$ cipi app create --octane=frankenphp   # explicit (same as --octane)

See Laravel Octane for requirements, convert, and how the vhost differs from FPM.

Non-interactive (flags)

bash
$ cipi app create \
    --user=myapp \
    --domain=myapp.com \
    --repository=git@github.com:you/myapp.git \
    --branch=main \
    --php=8.5

# Laravel Octane (FrankenPHP) — v5.0+
$ cipi app create --user=myapp --domain=myapp.com \
    --repository=git@github.com:you/myapp.git --octane

# optional PostgreSQL (v4.8.0+, after cipi db install pgsql)
$ cipi app create --user=myapp --domain=myapp.com \
    --repository=git@github.com:you/myapp.git --engine=pgsql
--userLinux username for the app. Must be unique, lowercase, alphanumeric.
--domainPrimary domain (e.g. myapp.com). Used for Nginx vhost and APP_URL.
--repositorySSH Git URL (e.g. git@github.com:you/repo.git). Must be SSH, not HTTPS.
--branchBranch to deploy. Defaults to main.
--phpPHP version for this app (e.g. 8.5). Defaults to PHP 8.5. Must be an installed version — only 8.3, 8.4 and 8.5 are accepted since v4.5.4 (Deployer 8 requires PHP ≥ 8.3).
--octane / --octane=frankenphpServe HTTP via Laravel Octane (FrankenPHP) instead of PHP-FPM (since v5.0). Laravel apps only — rejected with --custom. See Laravel Octane.
--engineDatabase engine: mariadb (default) or pgsql (since v4.8.0; requires cipi db install pgsql). Interactive create prompts when PostgreSQL is installed. Writes matching .env / connection URL.
If a GitHub or GitLab token is configured, Cipi automatically adds the SSH deploy key and creates the webhook on the repository — no manual steps needed. See Git auto-setup for setup instructions and fallback options when auto-configuration is not available.

Laravel Octane (FrankenPHP)

Since v5.0, Laravel apps can serve HTTP via Laravel Octane with the FrankenPHP server instead of a per-app PHP-FPM pool. Octane and classic FPM apps run side by side on the same Cipi server.

What Cipi configures

  • Allocates a localhost port in 8100–8999 and stores octane / octane_port in apps.json
  • Nginx vhost uses proxy_pass to Octane and serves static files from current/publicno per-app FPM pool
  • Supervisor program ${app}-octane alongside queue workers
  • Deployer template laravel-octane.php restarts / reloads Octane on deploy
  • .env: OCTANE_SERVER=frankenphp, OCTANE_HTTPS=true

App requirements

Your Laravel repository must include laravel/octane and run php artisan octane:install --server=frankenphp. Octane starts after the first successful deploy.

bash
# create an Octane app
$ cipi app create --user=myapp --domain=myapp.com \
    --repository=git@github.com:you/myapp.git --octane

# convert an existing FPM app → Octane (or the reverse)
$ cipi app convert myapp --to=octane
$ cipi app convert myapp --to=fpm

# tune Octane workers (also see app limits)
$ cipi app limits myapp --octane-workers=4

cipi app convert --to=octane|fpm rewrites the pool / vhost / Supervisor / Deployer template / .env, and re-applies SSL when a certificate is already present.

Octane tip: keep long-running queue work on cipi worker or Horizon — Octane handles HTTP only. Pair with Reverb for WebSockets.

cipi app create --custom

Creates a custom app with classic deploy (no zero-downtime): code is deployed into htdocs — no current/shared symlinks. Ideal for static sites, SPAs (Vue, React, Svelte), WordPress, other CMS, or any non-Laravel PHP project or framework.

During creation you only choose the document root (default /, or e.g. www, dist, public). Nginx is pre-configured with index index.html index.php, try_files $uri $uri/ /index.php?$args, and error_page 404 /404.html — no prompts for try_files or entry point.

Git optional (SFTP-only)

The Git repository is optional for custom apps. Laravel apps still require a repository. If you skip the repository for a custom app, Cipi creates /home/<app>/htdocs with a placeholder index.html and does not configure a deploy key or webhook — you upload files with SFTP (or SCP/rsync) to ~/htdocs as the app user. The onboarding output explains this “no repo — SFTP only” workflow. If you do provide a repository, behavior is unchanged: use cipi deploy <app> to pull code into htdocs.

What’s included and what you add

Custom apps have no database, no .env, no cron, and no queue workers. When a repository is configured, a deploy key and (with Git auto-setup) webhook are shown; for SFTP-only apps, those are omitted. The post-creation summary lists SSH access and next steps accordingly.

If your custom app needs a database (e.g. WordPress, Drupal), create one with cipi db create --name=<app> after deployment. See cipi db for backup, restore, and password management.

With Git — non-interactive example:

bash
$ cipi app create --custom --user=mysite --domain=mysite.com \
    --repository=git@github.com:you/mysite.git --docroot=dist

SFTP-only — omit --repository and --branch:

bash
$ cipi app create --custom --user=mysite --domain=mysite.com --docroot=dist

With a repository, use cipi deploy <app> to deploy; code is cloned into /home/<app>/htdocs.

app list / app show / app edit / app delete

Command Description
cipi app list List all apps with domain, PHP version, and status ((suspended) when offline)
cipi app show <app> Full details: domain, PHP, deploy key, workers, webhook, suspend state. For custom apps: type "Custom", docroot; webhook (and deploy key) omitted when SFTP-only without a repository.
cipi app edit <app> --php=8.5 Hot-swap PHP version. Updates FPM pool, Nginx socket, Supervisor, crontab, Deployer config, and .env — zero downtime
cipi app edit <app> --branch=develop Change the deploy branch
cipi app edit <app> --domain=new.example.com Rename the primary domain (since v4.6.2). Validates format and uniqueness, moves the old primary to aliases, regenerates the Nginx vhost, updates APP_URL, refreshes Git webhooks when auto-configured, and re-issues Let's Encrypt when a certificate already existed
cipi app edit <app> --repository=<SSH-URL> Attach or change the Git repository (e.g. enable deploy on a custom SFTP-only app created without --repository). Composable with --branch, --php, and --domain
cipi app edit <app> --node-build='npm ci && npm run build' Run a Node build after Composer vendors on every deploy (since v5.0). Deployer executes .deployer/node-build.sh (fail-closed; command is validated). Clear with --no-node-build
cipi app convert <app> --to=octane|fpm Convert between PHP-FPM and Laravel Octane (since v5.0). See Laravel Octane
cipi app env <app> Open the app's .env file in nano as the app user. Exits with error for custom apps (no .env).
cipi app reset-password <app> Regenerate the app's Linux user SSH password. The new password is displayed on screen — save it immediately
cipi app reset-db-password <app> Regenerate the app's database password (MariaDB or PostgreSQL per app engine) and automatically update DB_PASSWORD in the app's .env. Exits with error for custom apps (no database).
cipi app delete <app> Permanently remove the app, user, database (if Laravel), Nginx vhost, FPM pool, and Supervisor workers. For custom apps, skips database drop (none was created). Asks for confirmation.
cipi app delete <app> --force Same as delete but skips the confirmation prompt — for scripts, the panel API, and cipi-cli

cipi app reverb

Since v5.0, enable Laravel Reverb for first-party WebSockets on a Laravel app. Cipi allocates a localhost port in 9000–9099, adds Supervisor program ${app}-reverb, proxies Nginx /app to that port, and writes REVERB_* in .env.

bash
$ cipi app reverb enable myapp
$ cipi app reverb status myapp
$ cipi app reverb disable myapp

cipi app clone

Since v5.0, clone an existing Laravel app into a new staging (or review) app with its own domain. Sets cloned_from in apps.json; does not copy webhook or git IDs.

bash
$ cipi app clone myapp --domain=staging.myapp.com
$ cipi app clone myapp --domain=staging.myapp.com --name=myapp-stg --branch=develop --with-db
$ cipi app clone myapp --domain=staging.myapp.com --no-db
--domainRequired. Primary domain for the new app.
--nameOptional Linux username for the clone.
--branchDeploy branch for the clone (defaults to the source app's branch).
--with-dbCreate a fresh database for the clone.
--no-dbSkip database provisioning for the clone.

cipi app limits

Since v5.0, set per-app resource limits with hard caps enforced by Cipi.

bash
$ cipi app limits myapp --fpm-max-children=20 --memory-limit=256M
$ cipi app limits myapp --octane-workers=4 --worker-procs=3
$ cipi app limits myapp   # show current limits
--fpm-max-childrenPHP-FPM pm.max_children for FPM apps.
--memory-limitPHP memory_limit.
--octane-workersOctane worker count for FrankenPHP apps.
--worker-procsSupervisor queue worker process count.

app suspend / app unsuspend

Available since v4.5.8. Take an app offline without deleting it — useful for billing holds, maintenance windows, or staging sites you want fully dark. Suspending swaps the Nginx vhost for a static HTTP 503 page served from /var/www/cipi-suspended/.

bash
$ cipi app suspend myapp      # take offline (503 page)
$ cipi app unsuspend myapp    # restore normal vhost
Command Description
cipi app suspend <app> Sets suspended: true in apps.json, rebuilds the vhost to return 503 for all requests. Idempotent if already suspended.
cipi app unsuspend <app> Clears the flag, restores the normal Laravel/custom vhost, and reapplies SSL blocks. Idempotent if already online.

Behaviour

  • HTTPS included — certbot clones the suspension vhost into the :443 block, so HTTPS also shows the offline page
  • Let's Encrypt still works — the /.well-known/acme-challenge/ path stays public so certificates can be issued or renewed while suspended
  • Survives vhost regeneration — alias changes, PHP edits, and SSL installs respect the suspended flag
  • Visible in listingscipi app list marks suspended apps; cipi domains appends ⏸ suspended on each row
Suspend vs basic auth: cipi basicauth shows a login prompt but still runs your app. app suspend stops PHP entirely and serves a static offline page — visitors never reach Laravel. Use suspend for “site closed”; basic auth for “invite-only preview”.

Also available via the REST API (POST /api/apps/{name}/suspend), cipi-cli (apps suspend), and the WHMCS module (Suspend / Unsuspend buttons). Requires token ability apps-suspend for API access.

cipi basicauth

Available since v4.5.2. Protect any app — Laravel or custom — behind an Nginx username/password prompt. Useful for staging sites, internal tools, or apps that aren't ready for public traffic yet.

Command Description
cipi basicauth enable <app> [--user=NAME] [--password=PASS] Turn on HTTP basic auth. Credentials are generated when omitted and shown once on screen — save them immediately
cipi basicauth disable <app> Remove the prompt and clear stored credentials
cipi basicauth status <app> Show whether basic auth is enabled and the configured user

Credentials are hashed with openssl passwd -apr1 (no apache2-utils needed) and stored in /etc/nginx/cipi-basicauth/<app>.htpasswd; the enabled state lives in apps.json. The auth_basic directives are injected per location block, so protection survives vhost regeneration (alias changes, PHP edits) and is cloned into the :443 block by certbot — HTTPS is covered too. ACME challenges stay public, so certificate issuance and renewal are never blocked. Basic auth is removed automatically on cipi app delete.

This is distinct from cipi auth, which manages the Composer auth.json for private package repositories.

Managing ENV variables

Every Laravel app has a single .env file living at /home/<app>/shared/.env. Custom apps have no .env. It is created and pre-populated by Cipi during app create with the database credentials, APP_KEY, APP_URL, cache/session/queue settings, and the webhook token. The shared/ directory is symlinked into every release, so the same .env is always active regardless of which release is current.

Edit interactively via CLI

The safest way to change ENV values is through Cipi itself — it opens the file in nano as the app user, with the correct permissions:

bash
$ cipi app env myapp

Save with Ctrl+O then exit with Ctrl+X. Changes take effect immediately for new requests — no restart needed for most values. If you change queue connection or cache driver, restart the workers:

bash
$ cipi worker restart myapp

Edit directly via SSH

You can also edit the file directly over SSH as root or as the app user:

bash
# as root
$ nano /home/myapp/shared/.env

# or switch to the app user first
$ su - myapp
$ nano ~/shared/.env

Key ENV variables set by Cipi

Variable Description Set by
APP_KEY Laravel encryption key — generated once at app creation Cipi
APP_URL Updated automatically by cipi ssl install Cipi
DB_CONNECTION mysql for MariaDB (drop-in compatible), or pgsql when the app engine is PostgreSQL (v4.8.0+) Cipi
DB_DATABASE / DB_USERNAME / DB_PASSWORD Auto-generated credentials for the app's isolated database Cipi
CACHE_STORE database — uses the app's database Cipi
SESSION_DRIVER database Cipi
QUEUE_CONNECTION database Cipi
CIPI_WEBHOOK_TOKEN HMAC secret for cipi-agent webhook validation Cipi
CIPI_APP_USER Linux username owning this app Cipi
CIPI_MCP Enable or disable the built-in MCP server at /cipi/mcp User (true by default)
Do not change DB credentials manually. If you need to regenerate the database password use cipi db password myapp (or cipi app reset-db-password myapp) — it updates both the engine and the .env atomically. Editing them by hand risks leaving the two out of sync.

Adding your own variables

Add any custom variable at the bottom of the file as you normally would in a Laravel project. They are preserved across deploys because the .env lives in shared/ and is never overwritten by Deployer.

env
# your custom variables
STRIPE_KEY=sk_live_...
STRIPE_SECRET=sk_live_...
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org

cipi app logs

Tail application logs in real-time. Logs are rotated daily and kept for 14 days. By default, all logs are shown including Laravel daily logs (laravel-YYYY-MM-DD.log) from shared/storage/logs/.

bash
$ cipi app logs myapp                  # all logs (incl. Laravel daily logs)
$ cipi app logs myapp --type=nginx     # Nginx access + error
$ cipi app logs myapp --type=php       # PHP-FPM errors
$ cipi app logs myapp --type=worker    # queue worker output
$ cipi app logs myapp --type=deploy    # deploy history
$ cipi app logs myapp --type=laravel   # Laravel application logs
--type=nginxNginx access and error logs
--type=phpPHP-FPM error log
--type=workerSupervisor / queue worker output
--type=deployDeployer output — full deploy history with timestamps
--type=laravelLaravel application logs from shared/storage/logs/ (daily rotating laravel-YYYY-MM-DD.log)

app artisan & app tinker

Run Artisan commands and Tinker as the app user with the correct PHP version and open_basedir context — exactly as they would run during a deploy.

bash
$ cipi app artisan myapp migrate:status
$ cipi app artisan myapp queue:retry all
$ cipi app artisan myapp db:seed --class=ProductionSeeder
$ cipi app artisan myapp cache:clear
$ cipi app tinker myapp

SSH as the app user

Each app runs under its own isolated Linux user. Sometimes you need to work directly inside that user's environment — inspect files, run one-off scripts, or debug something that only reproduces as the correct user.

Direct SSH as app user (recommended)

App users can SSH directly to the server with the password generated at app creation:

bash
# connect as the app user (password auth)
$ ssh myapp@your-server-ip

# you are directly inside the app user's shell
myapp@server:~$ pwd
/home/myapp

myapp@server:~$ cd ~/current
myapp@server:~$ ls

The password is shown when the app is created (or use cipi app reset-password myapp to regenerate it). This works for SFTP clients, IDE remote sessions, and terminal access.

Via cipi (admin path)

If you are already connected as cipi, you can switch directly to any app user:

bash
$ ssh cipi@your-server-ip
cipi@server:~$ sudo su - myapp

myapp@server:~$ pwd
/home/myapp

Reset the app user password

If you need to regenerate an app user's password (e.g. for direct SSH or SFTP), use:

bash
$ cipi app reset-password myapp

The new password is displayed on screen — save it immediately.

Useful commands once logged in as the app user

bash
# navigate to the active release
myapp@server:~$ cd ~/current

# run artisan directly with the correct PHP version
myapp@server:~$ /usr/bin/php8.5 ~/current/artisan tinker

# inspect the shared .env
myapp@server:~$ cat ~/shared/.env

# tail all logs
myapp@server:~$ tail -f ~/logs/*.log

# check active releases (ll is a built-in alias for ls -al)
myapp@server:~$ ll ~/releases/

Every app user's .bashrc defines a handy ll='ls -al' alias (since v4.5.5) for quicker directory listings over SSH, alongside the deploy and composer shortcuts. Apps created before 4.5.5 receive the ll alias automatically on the next cipi self-update via the 4.5.5 migration.

The app user's open_basedir restriction limits PHP to /home/myapp. This is enforced at the PHP-FPM level, not at the shell level — you can access any file your shell user can read when working in the terminal.