@ddcn/cwcli
v0.1.3
Published
Work on your Cloudways WordPress sites locally with one command.
Maintainers
Readme
Cloudways WP Local CLI
Work on your Cloudways WordPress sites locally with one command.
- Authenticate to Cloudways
- List servers and apps
- Create clones on Cloudways
- Pull files + DB from Cloudways
- Spin up locally with Docker (Nginx, PHP-FPM, MariaDB, WP-CLI)
- Optionally push back to Cloudways
Quick Install (npm)
# Global install (scoped)
npm install -g @ddcn/cwcli
# Verify
cwl --helpNotes:
- The command is
cwl(primary). An aliascwcliis also available. - Default sites root is your current working directory. Override with
CWL_SITES_ROOT.
Authentication
You can authenticate interactively or via environment variables.
# Interactive re-auth (prompts for email + API key)
cwl auth
# View current auth status (email, storage method, token validity)
cwl auth status
# Logout (clears stored email/key/token)
cwl auth logoutEnvironment variables override stored values when set (useful for CI or temporary sessions):
export CW_EMAIL="[email protected]"
export CW_API_KEY="<your-api-key>"Sites root override:
export CWL_SITES_ROOT=/absolute/path/to/sitesEasiest Path (Single Command)
# Authenticate once (email + API key)
cwl auth
# One-step: pick the app → pull → start Docker → import DB → open admin (logged-in)
cwl quickThe first run asks you to pick the Cloudways app. It creates the site under your current directory as ./<slug> and opens http://localhost:8080 (or the port shown in the site’s .env).
Tip: need speed? Pull live (read-only) to skip server-side cloning:
cwl quick --liveSimple Workflow (Explicit Steps)
# 0) Check prerequisites (Docker Desktop, rsync, ssh)
cwl doctor
# 1) Authenticate (stores Cloudways API credentials)
cwl auth
# 2) List apps (find an APP_ID; try --sort name or --server <ID>)
cwl apps
# 3) Pull a site (creates ./<slug> in your current directory; omit --app to pick interactively)
cwl pull --app <APP_ID>
# Faster read-only from live (skips clone; exports live DB safely):
# cwl pull --app <PROD_APP_ID> --live
# 4) Start containers (Nginx, PHP-FPM, MariaDB, WP-CLI)
cwl up <site-name-or-path>
# 5) Import DB and rewrite URLs to http://localhost:<port> (from .env)
cwl db import <site-name-or-path>
# 6) Open in browser (add --admin to auto-login to /wp-admin/)
cwl open <site-name-or-path>Usage
- Global help:
cwl --help(See README.md for full usage instructions) - Per-command help:
cwl <command> --help - Common options used across commands:
--dir <dir>(deprecated): Explicit path to the site folder. Prefer positional site name or path.--port <port>: Local HTTP port (defaults to8080if not set in.env).--yes: Assume “yes” for prompts where supported.--live: Pull directly from the live app (read-only), where supported.
Site Folder Layout
After pulling or running quick/init, each site lives under your current directory as ./<slug> and contains:
wp/: WordPress files (public_htmlfrom Cloudways)..cw/: CLI artifacts likedb.sql[.gz],nginx.conf,meta.json, temp files.docker-compose.yml: Nginx + PHP-FPM + MariaDB + WP-CLI setup..env: Values likeWP_PORT,SITE_SLUG,DB_NAME.
You can run all cwl commands from anywhere using a positional site argument. If you pass a bare name, it resolves to ./<name> under your current directory (or under CWL_SITES_ROOT if set). You can also pass an explicit path. The legacy --dir still works but is deprecated.
Sites root:
- Default is your current working directory.
- Override with
CWL_SITES_ROOT:
export CWL_SITES_ROOT=/absolute/path/to/sitesCommand Reference + Examples
cwl quick
End-to-end: pull → up → import DB → open /wp-admin/ with auto-login.
# Let me pick an app interactively, use defaults
cwl quick
# Target a specific app, custom port, and auto-login as a user
cwl quick --app 123456 --port 8085 --user admin
# Fast read-only pull from live
cwl quick --live
# Filter list by server when prompting
cwl quick --server 7890cwl auth
Authenticate with Cloudways API. Credentials are stored securely when possible.
cwl auth
cwl auth --email [email protected] --key <API_KEY>
# Clear stored credentials
cwl auth --clearcwl servers and cwl apps
cwl servers
cwl apps
cwl apps --sort name # A→Z by app label
cwl apps --server 12345 # Filter to a specific servercwl pull
Pull files + DB for an app and scaffold Docker locally.
# Prompt to pick an app, create ./<slug>
cwl pull
# Explicit app and directory with custom port
cwl pull --app 123456 --dir ./my-site --port 8082
# Pull directly from live (read-only; exports live DB)
cwl pull --app 123456 --live --yes
# Use a single tar.gz stream (faster when master SSH is available)
cwl pull --app 123456 --archivecwl up / cwl down / cwl status
cwl up my-site # Start containers (resolves to ./my-site)
cwl up my-site --port 8090 # Change the local port and restart
cwl status my-site # Show URL and docker compose ps
cwl down my-site # Stop containers
# Explicit paths still work
cwl up ./my-site
cwl status ./my-site
cwl down ./my-sitecwl sites
List local sites under the default sites root. A directory is considered a site if it contains markers like docker-compose.yml, .cw/, or wp/.
cwl sitescwl db import
Import .cw/db.sql[.gz] into the local DB and rewrite URLs to your local host/port.
cwl db import my-sitecwl open and cwl admin
Open the local site or /wp-admin/ with an auto-login token. You can specify a username and token TTL.
cwl open my-site
cwl open my-site --admin --user editor --ttl 600
# Directly open /wp-admin/ auto-logged-in
cwl admin my-site --user admin --ttl 600cwl login
Generate a one-click login URL for the local site; print or open it.
cwl login my-site --user admin --ttl 600 --printcwl push
Push local wp/ and DB to a new or existing Cloudways app. The CLI will not push to your original live app; if the target equals the source (or pull was from live), a new clone is created automatically.
# Create a new clone automatically and push
cwl push my-site
# Push to an existing target app
cwl push my-site --to-app 222222
# Create with a custom label, then push
cwl push my-site --new-label staging-2025-09-12
# Files only or DB only
cwl push my-site --files-only
cwl push my-site --db-onlyDeleting local sites
Delete one site (stops Docker and removes volumes, including DB data), then removes the folder:
cwl rm my-site # prompts for confirmation
cwl rm my-site --yes # skip confirmationDelete all sites under the sites root. This stops Docker for each site, prunes volumes, and deletes the folders:
cwl rm-all # prompts for confirmation
cwl rm-all --yes # skip confirmationcwl ssh
Test SSH/SFTP connectivity for an app/server. Helpful for diagnosing rsync/SSH issues.
cwl ssh # pick an app
cwl ssh --app 123456cwl info
Prints raw Cloudways server/app info for debugging.
cwl info # servers overview
cwl info --app 123456 # details + credentials payloads (redacted upstream)Requirements
- macOS with Docker Desktop
- Node.js 18+
- SSH access to the Cloudways application user (password or SSH key). The CLI fetches the credentials via API.
How it works
- Auth: Exchanges your email + API key for a short-lived token; stored securely.
- Pull: Uses API to resolve SFTP/MySQL creds, rsyncs
public_htmlto./wp, dumps the DB to.cw/db.sql. - Local run: Generates
docker-compose.ymlandwp-config.php, starts Nginx/PHP/MariaDB/WP-CLI. - DB import: Loads
.cw/db.sqlinto MariaDB and rewrites the site URL tohttp://localhost:<port>. - Push: Never pushes to live. If the target is missing or equals the source (or your pull was from live), the CLI auto-creates a new clone on Cloudways and pushes there. It then runs a serialized-safe URL rewrite to the target domain.
Deauth/Reauth
- Reauth with new credentials:
cwl authand provide a different email/API key. - Deauth fully:
cwl auth logout(clears stored credentials and cached token). - Inspect current state:
cwl auth status. - Env override:
CW_EMAILandCW_API_KEYtake precedence over stored values.
Troubleshooting
- If rsync asks for a password and fails, install
sshpass(or copy your SSH key to the app user). - Start Docker Desktop before running
cwl uporcwl quick. - Re-auth with
cwl auth --clearthencwl auth.
Redis object cache drop-in locally
If your pulled site includes a persistent object cache drop-in (e.g., wp/wp-content/object-cache.php from Redis Object Cache or Object Cache Pro), WordPress may attempt to connect to a Redis server that isn’t present in the local Docker stack.
What we do by default:
- The CLI writes
wp/wp-config-local.phpwith:define('WP_CACHE', false);define('WP_REDIS_DISABLED', true);These ensure the drop-in stays inactive locally.
If you still see a Redis connection error:
- Confirm the defines exist in
wp/wp-config-local.php. - Stop and restart containers:
cwl down my-site && cwl up my-site. - Optionally delete/rename the drop-in:
mv wp/wp-content/object-cache.php wp/wp-content/object-cache.php.bak.
Live pull vs. clone
--livealways pulls read-only from your production app and exports the live DB. This is fastest and safest when you only need a local copy.- Without
--live, the CLI can clone your source app on Cloudways first to avoid touching production during pull/push workflows.
Docker folder permissions
- Symptom: "operation not permitted" or "mount source path ... permission denied" when starting containers.
- Fix: Allow Docker to access your project folder, then retry
cwl quick(ordocker compose up -din the site dir). - macOS: System Settings → Privacy & Security → Files and Folders → Docker: enable access for the folder (e.g., Desktop/Documents). Or Docker Desktop → Settings → Resources → File sharing → add your project path → Apply & Restart.
- Windows: Docker Desktop → Settings → Resources → File Sharing → add your project path → Apply & Restart. If using WSL2: Settings → Resources → WSL Integration → enable your distro.
- Linux: Ensure your user owns the project directory and has rw permissions. If SELinux is enabled, add
:Zto volume mounts (e.g.,./wp:/var/www/html:Z) or runsudo chcon -Rt svirt_sandbox_file_t /path/to/project.
From Source (Contributors)
git clone https://github.com/drewclifton/cwcli.git
cd cwcli
npm install
npm run start -- --helpPublishing (Maintainers)
Two options: GitHub Releases (CI) or manual CLI.
- GitHub Releases (CI)
- Add a repo secret
NPM_TOKENwith publish rights. - Bump version and push tag:
npm version patch -m "Release %s" git push origin HEAD --follow-tags - Create a GitHub Release for the new tag. The workflow publishes to npm.
- Manual CLI
npm login
npm publish --access public