Skip to content

Goal: do your routine Cloud Platform work (logs, databases, remote commands, deploys) from the terminal without opening the UI.

The jobs below are independent: orient yourself once, then jump to the one you need and stop there. Every command links to its reference entry for flags and defaults.

  • A reliable way to know which application and environment a command will act on, before it acts.
  • The highest-frequency jobs (logs, database pulls, remote Drush, SSH, artifact deploys, temporary environments) each reduced to one or two commands.
  • Non-interactive authentication for scripts and CI, and Composer hook points around any acli command.

Individual jobs need a little more (a local database server to import into, an SSH key registered with the platform, a composer.json at your project root). Each job below names its own before the first command.

Most acli commands act on an application or an environment. You can always pass one explicitly as app-name.env (for example myapp.dev), and you should for anything destructive. The env half is the environment’s name, not the label on its card, so the environment labeled Stage is myapp.test. For day-to-day work in a project checkout, link the directory once:

Terminal window
acli app:link

app:link writes the application UUID to .acquia-cli.yml in the project root; from then on, commands run in that directory default to the linked application (app:unlink removes it). To see every app.env alias you can target, run acli remote:aliases:list.

To check which environments an application actually has, ask the API with api:applications:environment-list (one of the generated api:* commands) and filter with jq, the standard JSON filter tool. It’s a separate install:

Terminal window
brew install jq

Then filter the environment list to just the names:

Terminal window
acli api:applications:environment-list myapp | jq '.[].name'
"dev"
"prod"
"test"

Those names are what you append to the application alias: myapp.dev, myapp.prod. An application with Continuous Delivery Environments or extra environments (ra, ama) lists those here too. Make this your pre-flight check: before anything that writes (deploying an artifact, creating or deleting an environment), confirm the alias you’re about to pass names the environment you think it does.

Watch an environment’s logs live, without opening the Cloud Platform user interface:

Terminal window
acli app:log:tail myapp.dev

app:log:tail prompts you to pick which logs to tail, as a comma-separated list:

  • Balancer, Varnish, and Apache requests
  • Apache and PHP errors
  • Drupal watchdog and requests
  • MySQL slow queries

It then streams Streaming has started and their output to your terminal as the servers receive new requests. Interact with the site in another window and watch the messages stream in; press Ctrl+C to stop. This is the fastest way to diagnose a live problem without touching the UI.

For which log answers which question, and how to download one instead of streaming it, see what the platform logs; for symptom-first triage using these logs, see diagnose a slow or erroring site.

Get a remote environment’s database, files, or both onto your local machine:

Terminal window
acli pull:db myapp.prod

pull:database imports the latest available backup into your local database, so you need a local database server to import into (the local dev quickstart sets one up). To download the dump without importing it, pass --no-import. The backup may be up to 24 hours old; if none exists, one is created. To force a fresh backup, see the --on-demand flag in the reference entry.

After the import, acli runs drush sql-sanitize, which scrambles user emails and passwords, so a production pull does not leave real credentials on your laptop. Two things switch it off: --no-scripts skips it explicitly, and --no-import implies --no-scripts, so a download-only pull never sanitizes either.

That second case covers ddev pull acquia: DDEV’s Acquia provider downloads with --no-import and imports the dump itself, so what lands in your local database is unsanitized. Run ddev drush sql-sanitize afterwards if you pulled from production.

The same shape works for Drupal public files with pull:files, and pull:all copies code, database, and files in one go. Local database connection settings come from the ACLI_DB_HOST, ACLI_DB_NAME, ACLI_DB_USER, and ACLI_DB_PASSWORD environment variables, already set for you in recommended local stacks like Lando (see configuration).

Open a shell on an environment, or run a single command and return, over SSH:

Terminal window
# Open a shell in the environment.
acli remote:ssh myapp.dev
# Run one command and return.
acli remote:ssh myapp.dev -- ls -al

remote:ssh needs an SSH key registered with the platform; acli ssh-key:create-upload does both halves in one step. The shell lands in the environment’s site root (/var/www/html), with the codebase, config/, docroot/, and hooks/ right there.

You don’t need Drush installed locally to run it on an environment:

Terminal window
acli remote:drush myapp.dev -- status --fields=db-status

Note the -- separating remote:drush’s own arguments from the Drush command and its options; it’s required.

On environments that harden PHP by disabling process execution, remote:drush (and the platform’s drush launcher shim) fail with pcntl_exec(): ... Permission denied. When that happens, run the site’s own drush.php through PHP over SSH instead. This is the same invocation the platform’s Cloud Hooks use, and it works where the launcher can’t:

Terminal window
acli remote:ssh myapp.dev -- \
'/usr/local/php8.4/bin/php \
/var/www/html/vendor/drush/drush/drush.php \
--root=/var/www/html/docroot status'

Point --root at the environment’s docroot/; swap status for any Drush command. For where drush itself fits next to acli, see Which CLI do I use?.

Cloud Platform environments run built code (vendor directories included) while your source repository typically ignores them. push:artifact bridges that gap. The artifact it commits includes vendor directories and scaffold files, even if they are gitignored in your source. From a project with a composer.json at its root, it runs composer install, removes sensitive files, commits the built result, and pushes it to your Acquia git remote:

Terminal window
acli push:artifact --destination-git-branch=main-build

The push alone deploys nothing: an environment picks up main-build only once it tracks that branch, so point one at it with acli api:environments:code-switch myapp.dev main-build --task-wait. A push to a branch no environment tracks succeeds silently and changes nothing anywhere.

To run extra build steps such as npm install inside the artifact build, add a post-install-cmd script to your composer.json. The reference entry has the details and the ACLI_PUSH_ARTIFACT_* environment variables for CI use; to run this deploy from CI instead of your terminal, see the CI/CD guide.

Spin up a Continuous Delivery Environment (CDE) for a branch, and tear it down when you’re done:

Terminal window
acli env:create pr-123 feature/my-branch
acli env:delete

See env:create and env:delete.

Many platform operations are asynchronous: the API returns immediately and the work happens in the background. app:task-wait blocks until a task finishes, which is what makes commands like api:environments:database-backup-create scriptable:

Terminal window
# my_db: your database name, from acli api:environments:database-list myapp.dev
BACKUP_TASK=$(acli api:environments:database-backup-create \
myapp.dev my_db)
# task-wait reads the task ID from the JSON the previous command printed
acli app:task-wait "$BACKUP_TASK"

For when a backup is worth taking on purpose and how to restore one, see the code-workflow guide’s rollback runbook.

To run your own script before or after any acli command, add a Composer script named pre-acli-<command> or post-acli-<command> (command name with dashes instead of colons) to your project’s root composer.json:

"scripts": {
"pre-acli-pull-database": [
"echo \"I'm pulling the database now!\""
],
"post-acli-pull-database": [
"echo \"I'm done pulling the database!\""
]
}

The naming pattern is (pre|post)-acli-(command name with dashes): pre-acli-push-database, post-acli-pull-files, and so on. A misnamed script silently never runs, so copy the command name exactly.

Use the command’s canonical name, not an alias. acli pull:db is an alias of pull:database, so the hook is pre-acli-pull-database; it fires whether you type acli pull:db or acli pull:database. pre-acli-pull-db never runs. Confirm the canonical name with acli <command> --help (the first line of the usage block) if you’re unsure, and see the reference for the full hook-point naming convention.

acli never prompts for login if credentials are already available. It checks, in priority order:

  1. An access token in the ACLI_ACCESS_TOKEN environment variable, with its corresponding ACLI_ACCESS_TOKEN_EXPIRY value.
  2. An ACLI_KEY environment variable and its corresponding ACLI_SECRET.
  3. Values stored in ~/.acquia/cloud_api.conf by acli auth:login.

For CI jobs and containers, pass an API token or key/secret pair (generated at cloud.acquia.com/a/profile/tokens) as environment variables, and add -n (--no-interaction) so nothing ever waits for input:

Terminal window
ACLI_KEY=<key> ACLI_SECRET=<secret> \
acli -n api:applications:list

Because the env vars outrank the config file, this also works on a machine where someone else’s credentials are stored. It also means a stale ACLI_ACCESS_TOKEN left in your shell wins over a perfectly good config file (see the token-expiry entry below). To put this to work in CI, see the CI/CD guide.

Cloud Platform API returned an error: The access token has expired. mid-session

your token expired between commands. Locally, run acli auth:login again with a fresh token. In a Cloud IDE, authentication uses refresh tokens tied to your Cloud Platform SSO session, and two IDE browser tabs open at once confuse the token refresh. Keep a single IDE tab open. If you exported ACLI_ACCESS_TOKEN in your shell, unset it: it outranks your valid config file (see the credential precedence order).

Your Cloud Platform API credentials are invalid.

the stored key/secret was revoked or mistyped. Run acli auth:login to reset your API credentials with a freshly generated token.

The command ran, against the wrong application or environment.

The worst failure produces no error at all. Two habits prevent it: run your pre-flight check from orientation above (acli api:applications:environment-list myapp | jq '.[].name', or acli remote:aliases:list) before anything destructive, and always pass the explicit app.env alias to writing commands (push:database, push:files, env:delete) instead of relying on the linked directory. Related error: Cloud Platform API returned an error: The application you are trying to access does not exist, or you do not have permission to access it.: either the alias is wrong, or (a documented known issue) the same SSH key is attached to two Cloud Platform accounts with access to the same subscription; give each account its own key.

A documented command or flag doesn’t exist in your acli

version mismatch. Compare acli --version against the version the reference states at the top, then update with self-update:

Terminal window
acli self-update
Downloading Acquia CLI (acquia/cli) 4.0.0
Download finished
Updating phar...
Successfully updated acli

remote:drush fails with pcntl_exec(): ... Permission denied

the environment restricts PHP process execution, so the Drush launcher can’t hand off to the site’s Drush. Use the php … drush.php over SSH form, which bypasses the launcher entirely.

There’s no acli push:code command.

Deployment through acli is artifact-based, via push:artifact. (A hidden push:code stub exists inside Cloud IDEs, but all it does is print a reminder to push code upstream with git.) If your source and deployed branches are the same, plain git push is the right tool, not push:artifact.

Your Composer hook never runs.

The script name didn’t match (pre|post)-acli-(command name with dashes) exactly, and a misnamed script fails silently with no error from acli. The two usual causes: colons left in place of dashes, or an alias used instead of the canonical command name (pull-db instead of pull-database). Run acli <command> --help and copy the canonical name from the first usage line.

  • acli command reference: every command’s flags, defaults, and exit codes.
  • Which CLI do I use?: acli doesn’t manage Pipelines, Drupal, or Canvas components; this table routes you to the tool that does.

Was this page helpful?