The calm way to run local WordPress.
Cove is a tiny CLI that spins up local sites in seconds β automatic HTTPS, one-click admin login, zero Docker. It bundles Caddy, FrankenPHP, MariaDB, and Mailpit into one self-contained toolchain for WordPress and plain static sites, with a dashboard at https://cove.localhost for your sites, their mail, their databases, their logs, and their files.
- Simple CLI: Manage everything from your terminal with a handful of short commands.
- Web Dashboard: A built-in GUI at
https://cove.localhostwith five views. Sites to view, filter, sort, add, and delete sites with one-click admin logins; Mail, an inbox of everything your sites send, scoped per site; Databases, a browser with in-place editing and a SQL console; Logs, PHP errors per site,debug.log, and access logs, live; and Files, a file manager and editor over each site's directory. Every view has a real URL. - Automatic HTTPS: Every site is served over HTTPS using Caddy's internal CA β no cert wrangling.
- WordPress & Static Sites: Spin up a fresh WordPress install β any version, nightly, or a multisite network β or a plain static site with one command.
- Per-site PHP: Pin any site to an older PHP (
cove php mysite 8.2); it runs on a native php-fpm behind the same Caddy, with TLS, logs, and URLs unchanged. - WordPress Migration: Pull a remote site down via SSH (
cove pull) or push a local site up (cove push). - Database Management: Browse and edit any table from the dashboard, run SQL with
ββ΅, plus Adminer with passwordless auto-login for exports and schema work.cove db backupsnapshots every site;cove db listshows credentials. - Email Catching: Built-in Mailpit catches every outgoing email so you never risk sending a test to a real inbox. Read it in the dashboard, filtered by site, with reset and login links lifted out of each message β and nothing is ever pruned.
- Logs, Parsed: The shared PHP error log, each site's
debug.log, Caddy access logs, and the service logs, read backwards in chunks so size never matters, with repeats folded and stack traces a click away. - Traffic: Each site's access log, read as requests, page views, errors, response times, and slowest requests, over a day, a week, or all of it, without any tracking script.
- Import: Drop a backup zip on the add-site form, or
cove import mysite backup.zip, and a site is created and restored from it, URLs rewritten. - Snapshots:
cove snapshot mysitecopies a site's files and database in about a second (copy-on-write clones on APFS, btrfs, xfs); restore puts one back and keeps the state it replaced, so nothing is lost. - Plugins, Themes, Users, Cron: Manage a site's plugins and themes, log in as any user, run cron events, or type WP-CLI, all from the site's page in the dashboard.
- Files, In Place: Browse any site's directory from the dashboard, open a file in a plain-text editor and save with
βS, preview images, create, rename, delete, download, and drop files or whole folders onto the listing to upload them. - Custom Ports: Run Cove alongside Local, Studio, DevKinsta, or MAMP β pick alternative HTTP/HTTPS ports and Cove migrates stored WordPress URLs automatically.
- LAN & Mobile Testing:
cove lanexposes sites to your phone via Bonjour/mDNS for iOS app sync. - Tailscale Integration:
cove tailscale enablemakes every site reachable from any device on your tailnet. - Instant Public Sharing:
cove sharespins up a Cloudflare Tunnel so you can share a WIP site with a client in seconds. - Hosts File Automation: Cove manages
/etc/hostsentries for you β no manual editing. - Pretty Errors: Whoops renders beautiful PHP error pages with stack traces and editor integration.
- Health Check:
cove healthdiagnoses crashes, OPcache pressure, and on-disk hygiene, and recommends fixes without changing anything. - Menu Bar App:
cove menubar enableadds a tiny companion showing service status at a glance β a native menu bar app on macOS, a system tray app on Linux desktops β with start/stop, one-click site open or admin login, and quick links. - Custom Caddy Rules: Per-site directives for reverse proxies, auth, headers, or anything else Caddy supports.
- Web Server: Caddy / FrankenPHP
- Database: MariaDB
- Email Catching: Mailpit
- Error Handling: whoops
- CLI Beautification:
gum - Made for: WordPress and
WP-CLI
Run the following in your terminal to install cove.
bash <(curl -sL https://cove.run/install-cove.sh)On macOS, the installer will offer to install Homebrew first if it's not already present. On Linux, make sure curl is installed before running it.
To preview an unreleased build from the main branch β useful for verifying a fix before it's tagged β pass --main:
bash <(curl -sL https://cove.run/install-cove.sh) --mainOnce installed, this is the shortest path from zero to a working WordPress site:
cove add myblog # fresh WP install at https://myblog.localhost
cove login myblog # generates a one-time admin login URL
cove list # shows every site Cove manages
cove db backup # snapshots every site's database to .sqlOpen https://cove.localhost in your browser to see the dashboard. It answers only on the machine running Cove (private addresses too under WSL2, and tailnet addresses over cove tailscale); shared sites are unaffected. Fresh installs trust Cove's local certificate authority automatically; if a browser still warns you, run cove trust once (see Troubleshooting).
Cove provides a simple set of commands to manage your local environment.
| Command | Description |
|---|---|
cove add <name> [flavor] |
Creates a new WordPress site (<name>.localhost). The optional flavor says what goes inside it: a version (6.4.3, 6.9-RC1), nightly, latest (the default), or plain for a static site with no database. --multisite builds a subdirectory network, --multisite=subdomain a subdomain one (subsites get HTTPS automatically); --php=<ver> pins the PHP version. Every WordPress site is created with WP_ENVIRONMENT_TYPE set to local (so core and plugins that check wp_get_environment_type() treat it as a sandbox) and WP_DEBUG_LOG on, writing to wp-content/debug.log. |
cove clone <source> <new-name> |
Copies a site β files, database, and custom Caddy rules β under a new name, rewriting stored URLs to the new domain. Uses a copy-on-write clone on APFS and btrfs, so it's fast and the two copies share disk until one is written to. |
cove delete <name> [--force] |
Deletes a site's directory and its associated database. |
cove rename <old-name> <new-name> |
Renames a site, its directory, database, and runs wp search-replace so stored URLs (siteurl, home, serialized content) all update to the new domain. |
cove list [--totals] |
Lists all sites managed by Cove, including each one's WordPress version. Use --totals to show disk usage. |
cove core check |
Reports the WordPress version of every site, flagging releases wp.org marks outdated or insecure. |
cove core update <site> [version] |
Updates a site's WordPress core to the latest release, or to a specific version (which may be a downgrade). Use --all to update every site that's behind. |
cove login <site> [<user>] [--url=<subsite-url>] |
Generates a one-time login link for a WordPress site. On a multisite network, --url picks the subsite to log in to. |
cove network <site> [--format=json] |
Lists the sites on a multisite network: name, URL, and ID. |
cove import <site> <archive.zip|.tar.gz> |
Creates a new site from a backup archive β a Cove snapshot export, a Local export, or a host backup with a WordPress tree and a .sql dump anywhere inside β and rewrites its URLs. |
cove backup <site> [--out=<file.zip>] [--keep] |
Files and database as one zip in ~/Cove/Backups/, ready for cove import. |
cove snapshot <site> [create|list|restore <id>|delete <id>|export <id>] |
Point-in-time copies of a site's files and database under ~/Cove/Snapshots/<site>/, restorable in place; a restore snapshots the current state first so it can be undone. --note="β¦" labels one. |
cove screenshot <site> [--out=<file.png>] |
Captures the site's front page with a headless Chromium-family browser; the dashboard shows the capture on the site's page. --sweep [--max=N] [--idle-only] refreshes stale previews in the background (the watchdog does this while the desktop is idle). |
cove wp <site> <argsβ¦> |
Runs WP-CLI inside a site from anywhere, on the PHP the site is pinned to: cove wp mysite plugin list. |
cove path <name> |
Outputs the full system path to a site's public directory. |
cove url <name> |
Prints the full HTTPS URL for a site (including the port suffix when on alternative ports). |
cove php [<site>] [<version>|default] |
Per-site PHP version switching. Pinning routes a site through a native Homebrew php@<version> php-fpm behind Caddy; default returns it to FrankenPHP's bundled PHP. The pin travels with the site through clone and rename. |
cove log [<site>] [-f] |
Shows error logs. Use -f to follow logs in real-time. The dashboard's logs view shows the same files parsed, filtered by level, and scoped per site. |
| Command | Description |
|---|---|
cove pull [--proxy-uploads] |
Pulls a remote WordPress site into Cove via SSH. Use --proxy-uploads to proxy media instead of downloading. |
cove push |
Pushes a local Cove site to a remote WordPress site via SSH. |
cove transfer probe [site] |
Reports which tools the backup/restore engine behind pull and push will use on a host. The engine degrades gracefully β zip, then tar, then PHP; mysqldump, or WordPress's own $wpdb when there is no MySQL client. |
| Command | Description |
|---|---|
cove enable |
Starts the Caddy, MariaDB, and Mailpit background services. |
cove disable |
Stops all Cove background services. |
cove status |
Checks the status of all background services. |
cove reload |
Regenerates the Caddyfile and reloads the Caddy server. |
cove health |
Read-only diagnostic: service liveness, FrankenPHP process state and last exit, recent segfaults classified by cause, live OPcache pressure, and on-disk hygiene. Recommends fixes, changes nothing. |
cove menubar <enable|disable> |
Menu bar companion showing service status at a glance, with start/stop controls, a Sites menu (open a site, or log in to a WordPress site as admin), and quick links to the Dashboard, Adminer, and Mailpit. Native menu bar app on macOS (built locally with clang), system tray app on Linux desktops (python3 + GTK 3 + AyatanaAppIndicator3, offered via apt). Opt-in; no extra download. Originally by Robby McCullough. |
| Command | Description |
|---|---|
cove db backup |
Creates a .sql backup for every WordPress site. |
cove db list |
Shows database credentials for all WordPress sites. |
For browsing and editing, the dashboard's databases view lists every database with the site that owns it, opens WordPress databases on an overview (site URL, prefix, autoloaded option weight), and lets you page through any table, double-click a cell to edit it, and run SQL. Adminer at https://db.cove.localhost signs you in automatically for exports, imports, and schema changes.
| Command | Description |
|---|---|
cove ports [--http N --https N] |
Interactively reconfigure HTTP/HTTPS ports. Migrates every WordPress site's stored URLs via wp search-replace so existing sites keep working. Supports --dry-run and --skip-urls. |
cove memory [set <value>] |
Audits memory_limit across Cove's ini, the FrankenPHP web server, and every php on your PATH. set 2G bumps Cove's ini and offers to update each Homebrew/system ini. |
cove directive <add|update|delete|list> [site] |
Manages custom Caddyfile rules for a specific site. |
cove mappings <site> [add|remove] [domain] |
Manages additional domain mappings for a site. A leading *. (quote it: '*.mysite.localhost') answers on every subdomain. Also in the dashboard under a site's Domains⦠menu item. |
cove proxy <add|list|delete> |
Manages standalone reverse proxy entries in the Caddyfile. |
| Command | Description |
|---|---|
cove share [site] |
Creates a temporary public tunnel via Cloudflare (installs cloudflared on-demand). |
cove lan <enable|disable|status|trust> [site] |
Manages LAN access to sites for mobile app sync (Bonjour/mDNS). |
cove tailscale <enable|disable|status> |
Exposes sites to your Tailscale network via port-based routing. |
cove wsl-hosts |
(WSL only) Shows Windows hosts file setup instructions. |
| Command | Description |
|---|---|
cove install |
Installs and configures all required dependencies. |
cove trust |
Installs Cove's local root certificate into the system trust store and every browser certificate database it can find: Chrome/Chromium/Brave/Edge's shared ~/.pki/nssdb, Firefox profiles, snap and Flatpak browsers. Fresh installs run it automatically; re-run any time the root rotates. |
cove upgrade |
Upgrades Cove, FrankenPHP, and Adminer to the latest versions, and refreshes the managed bits (Whoops, the login helper, the watchdog) across every site. |
cove version |
Displays the current version of Cove. |
You can get help for any command by running cove <command> --help.
Cove can coexist with other local WordPress tools that already bind ports 80 and 443. When you run cove install and something else is listening on the default ports, Cove detects the conflict and offers a menu:
β οΈ Port Conflict Detected
Port 80 is in use by: Local
Port 443 is in use by: Local
β― Use alternative ports (8090 / 8453) β run alongside other tools
Pick custom ports
Proceed with 80/443 anyway
Cancel installation
Pick Use alternative ports and Cove will install on 8090 / 8453. Visit https://myblog.localhost:8453 β Caddy's auto-HTTPS handles the non-default port transparently.
You can switch back and forth at any time without losing work:
cove ports # interactive menu (Keep / Default / Custom)
cove ports --http 80 --https 443 # switch back to defaults
cove ports --http 8090 --https 8453 # switch to alternatives
cove ports --dry-run # preview the effect of a port changeWhen the HTTPS port changes, Cove walks every WordPress site under ~/Cove/Sites/ and runs wp search-replace to rewrite stored URLs (siteurl, home, serialized content, custom mappings) so existing sites keep working on the new port. Non-WordPress sites are skipped automatically.
Cove can proxy requests to any local service running on a port. This is useful for tools like OpenCode that provide a web interface.
For example, if you run opencode web which starts a server on port 4096, you can access it through Cove at https://opencode.localhost:
# Create the site (if it doesn't exist)
cove add opencode --plain
# Add a reverse proxy directive
cove directive add opencode.localhost "reverse_proxy 127.0.0.1:4096"Now https://opencode.localhost will proxy all requests to 127.0.0.1:4096, giving you HTTPS access to the local service.
To remove the proxy later:
cove directive delete opencode.localhostTailscale allows you to securely access your computer from any other device on your private network. If you have Tailscale installed on both your laptop and your phone, you can access your Cove sites from your phone.
# Enable Tailscale integration (auto-detects your hostname)
cove tailscale enable
# View the generated URLs for each site
cove tailscale statusCove automatically detects your Tailscale hostname. Each site gets a unique port (e.g., https://your-laptop.tail1234.ts.net:9001). Open these URLs on any device connected to your Tailscale network.
To disable:
cove tailscale disableThe web dashboard lives at https://cove.localhost (or https://cove.localhost:8453 on alternative ports). It has five views, one keystroke apart, plus a page for each site, and every one has a real URL you can bookmark or paste.
- Every site Cove manages, with its WordPress version (flagged when wp.org marks it insecure), PHP pin, disk usage, and last-modified time.
- Filter by name (press
/from anywhere) or by type (click aWP/STATICpill); sort by name, type, size, or last modified; pin the sites you are working on to the top. βKopens a command palette that searches sites and commands from anywhere:β΅opens a site's page,β§β΅the site itself,ββ΅a one-time admin login.- Add WordPress or plain sites from a form that offers real WordPress versions, or drop a backup zip on the form to import one; delete with an undo window, or bulk-delete everything a filter matches β behind a dialog that lists the sites and asks for their number to be typed.
- Click a row to open the site's page (below). Right-click any row for the menu: manage, open, one-time admin login, rename, reveal in Finder, browse files, database, PHP version, logs, mail, copy path, pin, delete.
- The
caddy,mariadb, andmailpitdots in the top bar open a panel with each service's status, version, ports, and credentials.
- One site on its own page: type, WordPress and PHP versions, size, last change, and path, with open and log in to admin beside them. A capture of the front page sits beside the tiles, taken by an installed Chrome, Chromium, Brave, Edge, Arc, or Vivaldi and refreshed when the site changes. Tiles lead to the site's files, database, mail, and logs; a manage row carries domains, PHP version, rename, reveal, copy path, backup zip, pin, and delete.
- On a multisite network the page lists every site on it with its own open and log in β a one-time link minted for that subsite, so it lands on the subsite's
wp-admin. The same is available from the terminal ascove network <site>andcove login <site> --url=<subsite-url>. - Tabs, each with its own URL: plugins and themes (status, version, waiting updates, auto-update; activate, deactivate, update, delete β network-wide on a multisite; check for updates asks wp.org on demand), users (roles, email, registered, and a one-time log in as any user, whatever the role; network-wide on a multisite, landing subsite-only users on a site they belong to), cron (every event with next run and recurrence, run now, run all due), traffic (requests, page views, errors, response times, bytes, cron, and mail from the site's access log, as tiles, a bar chart, top pages, slowest requests, 404s and 5xx, over 24 hours, 7 days, or the whole log), snapshots (take one with a note; restore, download as a zip, or delete any β a restore keeps a snapshot of the state it replaced), and wp-cli (a console: type,
β΅, output with exit code and timing, history onβ/β). Every row on these tabs has a right-click menu with its actions, plus browse-files and copy shortcuts. Escreturns to the list,ββ΅logs in.
- An inbox of every message your sites send, over Mailpit's API. Each message carries a site chip; one click narrows the inbox to that site, and a site's row menu lands there directly.
- The message opens beside the list with
html,text, andheadersa click apart. HTML renders in a sandboxed frame that runs no scripts and loads nothing remote; inline images come through, remote ones are counted and blocked. - Every link in a message is lifted into a row of chips with a copy button β most local mail exists to carry a password reset or login link.
- Mark unread, delete, mark all read, delete everything matching. New mail arrives live. Nothing is ever pruned: Cove launches Mailpit with no message cap.
- Every database with the site that owns it, table counts, and sizes. A WordPress database opens on an overview: site URL, home, prefix, theme, post and user counts, and the largest autoloaded options.
- Any table as a grid: column types in the header, click to sort, a search box for one column or all, paging. Tables with a primary key are editable in place β double-click a cell,
β΅saves,Esccancels,ββ«writesNULLβ and rows can be deleted. - A structure tab with columns, indexes, and the
CREATE TABLE; a sql console that runs several statements at a time withββ΅, shows results as grids or affected-row counts with timings, and keeps a history. - Adminer stays one click away, deep-linked to the open table, for exports, imports, schema changes, and users.
- The system logs on the left β the shared PHP error log, Caddy's process and reload logs, the watchdog, Mailpit, php-fpm β and below them every site, most recently active first.
- Pick a site for three tabs: php errors from the shared log scoped to that site, its own debug.log, and its Caddy access log. "View log" in a site's row menu opens whichever one the site actually writes to.
- Entries are parsed into a level chip, the message, and
file:line; consecutive repeats fold into one row with a count, and a click opens the stack trace. Filter by level, search, page back through older, or leave live on and watch the file grow. - Files are read backwards in chunks, so a 100 MB error log answers as fast as a small one.
- Every site on the left; pick one and its directory opens on the right, with a breadcrumb bar that walks back up and copies the full path from its last crumb. Folders first, sortable by name, size, or modified time; a filter box narrows the current folder; dotfiles can be hidden.
- Click a text file and it opens in an editor with syntax highlighting for PHP, JavaScript, CSS, HTML, JSON, SQL, shell, Markdown, and config files β Cove's own small tokenizer, nothing fetched from the network.
βSsaves, discard throws the edit away, wrap soft-wraps long lines,Tabinserts a tab. If the file changed on disk while it was open, save turns into overwrite? rather than silently clobbering the other change. Images preview in place; anything binary or over 2 MB offers a download instead. - + file and + folder add a row to type the name into (a name with slashes creates the folders on the way); rename and delete sit on every row, delete needing a second click; upload picks files, or drop files and whole folders onto the listing. reveal opens the folder in Finder or the desktop file manager.
- Right-click a row, the listing, or a crumb for a menu: open, rename, download, reveal, copy path, new file or folder here, upload here, delete. The site's own folder and
public/are off limits to file delete and rename; their menu offers Delete siteβ¦, which confirms and then stages the same delete the sites list does, undo window included. - Keyboard:
β/βorj/kto move,β΅to open,β«to go up,Escto close the file,/to filter,F2to rename,Deleteto delete. - Everything stays inside the site's directory: paths are checked segment by segment, symlinks are followed for reading and editing but never recursed into on delete, and files served for preview or download are sandboxed so nothing in a site can run as the dashboard.
- System, light, or dark theme β right-click the toggle to pick, and the choice is remembered.
- Views crossfade into each other, the wordmark returns you to sites without a reload, and Back and Forward walk through what you opened.
Every command runs without a terminal. When stdin is not a TTY, confirmations default to yes where that is safe (delete, push, directive delete, core update, cloudflared install) and a prompt that cannot be avoided stops with a message naming the flag to pass instead. The flags to know:
| Need | Use |
|---|---|
| Confirm a destructive step | --yes on delete, push, pull, core update, opcache set, memory set |
| Pick what a prompt would ask | pull --ssh "user@host -p 22" --site <name> --path <dir>, push --site <name> --ssh β¦, proxy add <name> <domain> <target>, tailscale enable <hostname>, install --http 80 --https 443 --db-root-user β¦ --db-root-pass β¦ |
| Machine-readable output | list --format=json, status --porcelain, login --plain, network --format=json, snapshot <site> list --format=json, share --format=json, backup --format=json |
| A tunnel without a foreground process | share <site> --background, then share <site> stop |
| Edit Caddy rules non-interactively | pipe them: printf 'header X-Test 1\n' | cove directive set <site> |
| Anything sudo needs on Linux | run that command with sudo (sudo cove upgrade); /etc/hosts lines are skipped and reported when nothing can ask for a password |
The dashboard's API answers only from this machine, so agents run the CLI, not the API.
Cove is built from modular source files that are compiled into a single distributable script.
cove/
βββ main # Core script: globals, helpers, the dashboard, and command routing
βββ commands/ # Individual command files (one per command)
βββ menubar/ # Menu bar apps (macOS native + Linux tray), embedded into cove.sh at compile time
βββ adminer-theme/ # The Cove theme for Adminer (fetched at install/upgrade)
βββ compile.sh # Combines main + commands + menubar into cove.sh
βββ cove.sh # Compiled output (auto-generated, do not edit directly)
βββ test-matrix.sh # Cross-distro release checks (needs a private test box)
βββ install-cove.sh # Standalone installer script
After making changes to main or any file in commands/, compile the distributable script:
./compile.shThe watch.sh script uses fswatch to monitor file changes and automatically runs compile.sh:
./watch.shTo test your local development version on Linux or WSL without publishing to GitHub:
-
Copy the project folder to your Linux machine or WSL environment
-
Compile the script (if not already done):
./compile.sh
-
Install using dev mode:
./install-cove.sh --dev
The --dev flag tells the installer to use the local cove.sh from the same directory instead of downloading from GitHub. This allows you to test your changes before publishing a release.
- macOS: Intel and Apple Silicon (via Homebrew)
- Linux: Ubuntu/Debian (apt) and Fedora/RHEL/CentOS (dnf)
- WSL2: Windows Subsystem for Linux (requires systemd enabled)
Cove issues its own local certificates via Caddy's internal CA. Fresh installs trust it automatically; if a browser still warns you, run cove trust β it drops the root into the system store and every Firefox/Chromium profile it can find. Failing that, you have two options:
- Click through once per site β click Advanced β Proceed to β¦ and the browser will cache the decision.
- Trust Caddy's root CA system-wide by hand. The CA cert lives at:
- macOS:
~/Library/Application Support/Caddy/pki/authorities/local/root.crtβ usually auto-trusted by Caddy on install. - Linux (Ubuntu/Debian):
sudo cp ~/.local/share/caddy/pki/authorities/local/root.crt /usr/local/share/ca-certificates/caddy.crt sudo update-ca-certificates
- macOS:
Cove's installer detects this and offers the reconfiguration menu described in Running Alongside Local, Studio, or DevKinsta. If you skipped the prompt or want to change ports later, run cove ports.
Cove needs systemd for service management (Caddy, MariaDB, Mailpit). Enable it by adding to /etc/wsl.conf inside your WSL distro:
[boot]
systemd=trueThen from a Windows PowerShell: wsl --shutdown, and restart your WSL session.
WSL2 has its own virtual network, so myblog.localhost doesn't resolve from Windows by default. Run cove wsl-hosts inside WSL and follow the PowerShell snippet it prints to update Windows' hosts file.
Make sure MariaDB is running (cove status) and that ~/Cove/config contains a valid DB_USER and DB_PASSWORD. If MariaDB won't start on macOS, try brew services restart mariadb and then re-run cove enable.
If you used --skip-urls during cove ports, the WordPress siteurl / home options still point at the old port. Re-run cove ports without --skip-urls (even changing back and forth works) and Cove will run wp search-replace to realign everything. For one-off fixes, you can also run wp option update siteurl https://yoursite.localhost:8453 from inside the site's public/ directory.
Cove is open-source software licensed under the MIT License. Copyright (c) 2025-present, Austin Ginder.