Apps
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, Nginx vhost, MariaDB database, 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)
$ cipi app create
Non-interactive (flags)
$ cipi app create \
--user=myapp \
--domain=myapp.com \
--repository=git@github.com:you/myapp.git \
--branch=main \
--php=8.5
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:
$ cipi app create --custom --user=mysite --domain=mysite.com \
--repository=git@github.com:you/mysite.git --docroot=dist
SFTP-only — omit --repository and --branch:
$ 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 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 MariaDB password and automatically update the
DB_PASSWORD value in the app's .env file. 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
|
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/.
$ 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
:443block, 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 listings —
cipi app listmarks suspended apps;cipi domainsappends⏸ suspendedon each row
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.
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:
$ 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:
$ cipi worker restart myapp
Edit directly via SSH
You can also edit the file directly over SSH as root or as the app user:
# 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 | Always mysql (MariaDB is drop-in compatible) |
Cipi |
| DB_DATABASE / DB_USERNAME / DB_PASSWORD | Auto-generated credentials for the app's isolated database | Cipi |
| CACHE_STORE | database — uses the app's MariaDB |
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) |
cipi db password myapp — it updates both MariaDB 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.
# 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/.
$ 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
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.
$ 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:
# 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:
$ 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:
$ cipi app reset-password myapp
The new password is displayed on screen — save it immediately.
Useful commands once logged in as the app user
# 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.
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.