Installing Cipi

Cipi installs a complete Laravel-ready production stack on Ubuntu 24.04 or 26.04 with a single command. The installer takes roughly 10 minutes and sets up Nginx, MariaDB, PHP, Valkey (optional PostgreSQL via cipi db install pgsql after install), Supervisor, Fail2ban, UFW, Certbot, Deployer, and the cipi CLI itself.

From zero to production in three steps

bash
# 1 — on a fresh Ubuntu 24.04 / 26.04 VPS, over SSH, as a user with sudo
wget -O - https://cipi.sh/setup.sh | bash

# 2 — create the app (Laravel by default)
cipi app create

# 3 — deploy it and put it behind HTTPS
cipi deploy myapp
cipi ssl install myapp

That is the whole path. First create an SSH key on your computer (below), then run the installer on the server — it will ask you to paste that public key. Everything after that is explained in the rest of this page.

Run the installer on the server, not on your own machine. SSH into the fresh VPS first and run it there, as a user with sudo. Cipi provisions the whole box — it is not something you point at a remote host from your laptop.

Create an SSH key on your computer

Do this on your laptop or workstation, not on the VPS. The installer needs the public half of the key so it can lock down the server: after setup you SSH in as the cipi user with this key. Root SSH login is disabled. Never paste the private key.

See if you already have one:

bash
ls ~/.ssh/*.pub

If id_ed25519.pub or id_rsa.pub is listed, you already have a key — skip generation and copy the public file with the command below. If the folder is empty or the command errors, create a new Ed25519 key:

bash
ssh-keygen -t ed25519 -C "you@example.com"

Press Enter to accept the default path (~/.ssh/id_ed25519). Set a passphrase if you want (recommended). On macOS and Linux run this in Terminal; on Windows use PowerShell or WSL.

Show the public key — this is the single line you will paste when the installer asks:

bash
cat ~/.ssh/id_ed25519.pub

It looks like ssh-ed25519 AAAA… comment. Copy the whole line:

  • macOS: pbcopy < ~/.ssh/id_ed25519.pub
  • Linux: xclip -sel clip < ~/.ssh/id_ed25519.pub (or copy from the terminal)
  • Windows (PowerShell): Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | Set-Clipboard
Only the .pub file is public. The file named id_ed25519 (no extension) is the private key — keep it on your computer and never paste it into the installer or send it to the server.

Run the installer on the server

SSH into the fresh Ubuntu VPS as a user with sudo, then:

bash
wget -O - https://cipi.sh/setup.sh | bash

The wizard asks for the public key before any package is installed. Paste the line you copied above (accepted formats: ssh-ed25519, ssh-rsa, ecdsa). Cipi sanitizes comments, carriage returns, and extra whitespace, then creates a dedicated cipi Linux user for admin SSH access and hardens SSH: root login disabled, cipi public-key only (group cipi-ssh), app users (group cipi-apps) can connect with password. Login attempts are limited to 3 with a 20-second grace period; X11 forwarding is disabled.

The installer also generates a random 40-character root password, stores it in /etc/cipi/server.json, and displays it in the installation summary. It is not an SSH password: root SSH login is disabled. It is what you type when, already logged in as the cipi user with your key, you run su root. Keep it in a password manager.

Non-interactive installation

For automated setups, pass your SSH public key via the SSH_PUBKEY environment variable to skip the interactive prompt:

bash
SSH_PUBKEY="ssh-ed25519 AAAA..." wget -O - https://cipi.sh/setup.sh | bash

AWS (root login disabled by default)

bash
$ ssh ubuntu@your-server-ip
$ sudo -s
$ wget -O - https://cipi.sh/setup.sh | bash

At the end of the installation you will see a summary screen with the SSH access details, the auto-generated root password, and the MariaDB root password. Save them immediately — they are shown only once. Credentials are stored in /etc/cipi/server.json, which — like all Cipi configuration files — is encrypted at rest using AES-256-CBC via the built-in Vault system. Credentials, SSH keys, .env files, and database dumps are protected both on disk and during transfer (via Sync encrypted archives). Cipi also enforces GDPR-compliant log retention with automatic rotation policies for application, security, and HTTP logs — see Log retention for details.

Save the SSH access details, root password, and MariaDB root password. Shown only once during installation. Credentials are stored encrypted at /etc/cipi/server.json.

Post-installation access

After installation, admin access uses public-key authentication as the cipi user (root login is disabled). App users can SSH directly with ssh myapp@server-ip and the password generated at app creation. To run cipi commands or perform any administrative task, connect as cipi and then escalate:

bash
# 1. connect to the server (key-based auth only)
$ ssh cipi@your-server-ip

# 2. escalate to root to run cipi commands
cipi@server:~$ sudo -s

# 3. now you can use all cipi commands
root@server:~# cipi status
root@server:~# cipi app list

# 4. to work as an app user, switch with su
root@server:~# su - myapp

Requirements

  • Ubuntu 24.04 or 26.04 LTS (fresh installs only; interim releases are not supported)
  • Root access (or sudo -s on AWS)
  • Ports 22, 80, and 443 open
  • A clean server — do not install Cipi on a server with an existing web stack
  • A public internet-facing server with a routable public IPv4 address — NAT-only VPS, home networks behind a router, and hosts not reachable from the internet are not supported
  • Full KVM or bare-metal virtualization — not OpenVZ, LXC containers, or shared-kernel environments (including local Docker, Docker Desktop, OrbStack, Colima, WSL, and similar desktop container runtimes)
Cipi does not run locally. It is not compatible with local dockerized environments. The installer may appear to succeed on a laptop VM or container, but SSL provisioning, domain routing, and production workflows will not work without a real public server. See Why not local? for the full explanation.

Cipi is tested and works on: DigitalOcean, AWS EC2, Vultr, Linode / Akamai, Hetzner, Google Cloud, OVH, Scaleway, and any KVM or bare-metal host running Ubuntu with a public IP.

Quick Start

1. Create your first app

The interactive wizard asks for a username, primary domain, Git repository URL (SSH format) and branch when applicable, and PHP version. Laravel apps always require a repository; for custom apps you can leave the repository empty to host the site via SFTP only — see custom apps. Since v5.0, Laravel apps can use classic PHP-FPM or Laravel Octane (FrankenPHP) with --octane — see Laravel Octane.

bash
$ cipi app create
$ cipi app create --octane   # Laravel Octane (FrankenPHP)

Or pass all flags directly to skip interactive mode:

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

At the end, Cipi prints a credentials summary — save it, shown only once — including the server's public IP address (for DNS configuration), the SSH deploy key, database credentials, a ready-to-use mariadb+ssh:// connection URL for GUI clients (TablePlus, DBeaver, Sequel Pro), webhook URL, and webhook token.

2. Add the deploy key to your Git provider

If you have configured a token for your Git provider via cipi git — GitHub, GitLab, Cursor Origin, AWS CodeCommit, Bitbucket Cloud or Azure DevOps since v5.2.3 — this step is automatic — Cipi adds the deploy key and creates the webhook for you. See Git auto-setup for details.

Otherwise, copy the ssh-ed25519 ... key shown after app creation and add it as a Deploy Key in your repository:

  • GitHub: Repository → Settings → Deploy keys → Add deploy key
  • GitLab: Repository → Settings → Repository → Deploy keys

3. Prepare your Laravel project

Cipi uses the database driver for cache, sessions, and queues. Run these once inside your Laravel project, commit, and push the generated migrations:

bash
$ php artisan cache:table
$ php artisan session:table
$ php artisan queue:table
$ php artisan migrate
Cipi automatically runs artisan migrate --force on every deploy. The cache, session, and queue tables will be created on first deploy if you commit the migrations.

4. Deploy

bash
$ cipi deploy myapp

Deployer clones your repo into a new releases/N/ directory, runs composer install --no-dev, links .env and storage/, runs migrations, runs artisan optimize, creates storage:link, swaps the current symlink atomically, and restarts queue workers. Zero downtime.

5. Install SSL

bash
$ cipi ssl install myapp

Certbot provisions a Let's Encrypt certificate, configures Nginx for HTTPS, and updates APP_URL in .env. Your Laravel app is now live on https://myapp.com.

Tech Stack

Cipi brings a complete, production-ready stack to your Ubuntu server. Here is everything that gets installed and configured:

Component Role
Ubuntu Base OS (24.04 or 26.04 LTS)
Nginx Web server, reverse proxy, SSL termination. Since v4.5.9, mainline nginx from nginx.org (1.29.8+) with max_headers 1000 for HTTP/2 hardening — not the Ubuntu archive package
PHP-FPM Default PHP runtime per app (8.3–8.5; ondrej/php, packages.sury.org, or Ubuntu main per release)
Laravel Octane Optional FrankenPHP HTTP server per Laravel app (since v5.0) — cipi app create --octane; FPM and Octane apps coexist on the same server
MariaDB Default relational database (drop-in MySQL replacement, port 3306). Optional PostgreSQL via cipi db install pgsql (port 5432) since v4.8.0
Valkey In-memory store for cache, sessions, queues, broadcast (BSD-licensed Redis fork)
Supervisor Process manager for Laravel queue workers, Octane, Horizon, and Reverb
Deployer Zero-downtime deployment tool
Certbot Let's Encrypt SSL certificates
UFW Firewall (ports 22, 80, 443)
Fail2ban Progressive brute-force protection with recidive jail
unattended-upgrades Automatic security patches
Composer PHP dependency manager
cipi CLI Server management and orchestration

What every app gets

Each app runs in a fully isolated environment. Laravel is the default — zero-downtime deploys, its own database, workers, scheduler and webhook — optionally served by Octane (FrankenPHP) instead of PHP-FPM. --custom is for simple sites (WordPress, static + PHP): a classic deploy into htdocs, a configurable docroot, no database, no .env, no cron, no workers.

Area What Cipi gives you
Web server Nginx with a per-app virtual host — PHP-FPM or Octane via proxy_pass, tuned for Laravel
PHP & Composer Selectable per app, hot-swappable; settings managed with cipi ini across FPM and CLI
Database MariaDB by default, optional PostgreSQL — a dedicated database and user per Laravel app
Queue workers Supervisor pools per app — queue:work or Horizon; optional Reverb for WebSockets
Deployments Deployer — atomic symlink, 5 releases, rollback, optional Node build; classic clone into htdocs for custom apps
SSL Let's Encrypt via Certbot — HTTP-01 by default, optional DNS-01 (Cloudflare) with wildcards
Security Fail2ban + UFW, a dedicated Linux user, its own FPM/Octane process and SSH key per app; opt-in CrowdSec and Cloudflare Zero Trust
Healthchecks HTTP probes every 5 minutes, plus a post-deploy check with optional automatic rollback of a broken release; since 5.3, cipi monitor watches disk, services, workers, 5xx spikes and load
Backups Backup profiles — what, how often, where, how long — S3 / S3-compatible / local, with client-side encryption
Configuration Optional per-project cipi.yml for aliases, databases, workers, healthcheck and backups

App Structure

When you run cipi app create, Cipi creates a fully isolated environment for the app. Here is everything that gets set up:

/home/myapp/ ← isolated Linux user (chmod 750)
├── .ssh/
│   ├── id_ed25519 ← deploy key (private)
│   └── id_ed25519.pub ← deploy key (public — add to Git)
├── .deployer/
│   └── deploy.php ← Deployer config (auto-generated)
├── current -> releases/3/ ← symlink to active release
├── releases/
│   ├── 1/
│   ├── 2/
│   └── 3/ ← latest release (last 5 kept)
├── shared/
│   ├── .env ← auto-compiled by Cipi
│   └── storage/ ← persistent storage
└── logs/
    ├── nginx-access.log
    ├── nginx-error.log
    ├── php-fpm-error.log
    ├── worker-default.log
    └── deploy.log

In addition to the home directory, Cipi creates these system files:

PHP-FPM pool: /etc/php/8.5/fpm/pool.d/myapp.conf (user=myapp; omitted for Octane apps)
Octane (v5+): Supervisor myapp-octane + Nginx proxy_pass → 127.0.0.1:81xx
Nginx vhost:  /etc/nginx/sites-available/myapp
Supervisor:   /etc/supervisor/conf.d/myapp.conf
Crontab:      * * * * * php artisan schedule:run
MariaDB:      database 'myapp', user 'myapp'@'localhost'

The .env is auto-compiled with all credentials — database name, password, webhook token, and APP_KEY. You never have to touch it manually, but you can always edit it with cipi app env myapp.

If someone unpacks a zip as root or chmods .ssh to 775, cipi app fix-permissions (since v5.2.1) restores this layout. A blunt chown -R app:app is wrong: nginx writes vhost logs as www-data into logs/ (2775 setgid), the home must stay 750 so www-data (in the app group) can read the docroot, and .ssh must be 700 or OpenSSH StrictModes refuses the deploy key.