Everyday workflows
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.
What you’ll have when you’re done
Section titled “What you’ll have when you’re done”- 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.
Prerequisites
Section titled “Prerequisites”- acli installed and authenticated
- Access to at least one Cloud Platform application with a dev environment you’re allowed to experiment on
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.
Know what you’re pointed at
Section titled “Know what you’re pointed at”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:
acli app:linkapp: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:
brew install jqsudo apt-get install jqOn other distributions, jq is in the standard package repositories under the same name.
Then filter the environment list to just the names:
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.
- Tail logs from an environment: stream live log output while you reproduce a problem.
- Pull a database or files: import a remote environment’s data into your local stack.
- Run Drush remotely or open a shell: run Drush commands or a one-off shell command against an environment, no local Drush install needed.
- Deploy code as an artifact: push a built artifact to your Acquia git remote and point an environment at it.
- Temporary environments: spin up and tear down a Continuous Delivery Environment, and block on asynchronous API tasks.
- Hook scripts around any acli command: run your own script before or after any
aclicommand via Composer. - Non-interactive authentication: pass credentials as environment variables so CI never waits for a login prompt.
Tail logs from an environment
Section titled “Tail logs from an environment”Watch an environment’s logs live, without opening the Cloud Platform user interface:
acli app:log:tail myapp.devapp: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.
Pull a database or files
Section titled “Pull a database or files”Get a remote environment’s database, files, or both onto your local machine:
acli pull:db myapp.prodpull: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).
Run Drush remotely or open a shell
Section titled “Run Drush remotely or open a shell”Open a shell on an environment, or run a single command and return, over SSH:
# Open a shell in the environment.acli remote:ssh myapp.dev
# Run one command and return.acli remote:ssh myapp.dev -- ls -alremote: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:
acli remote:drush myapp.dev -- status --fields=db-statusNote 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:
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?.
Deploy code as an artifact
Section titled “Deploy code as an artifact”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:
acli push:artifact --destination-git-branch=main-buildThe 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.
Temporary environments
Section titled “Temporary environments”Spin up a Continuous Delivery Environment (CDE) for a branch, and tear it down when you’re done:
acli env:create pr-123 feature/my-branchacli env:deleteSee 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:
# my_db: your database name, from acli api:environments:database-list myapp.devBACKUP_TASK=$(acli api:environments:database-backup-create \ myapp.dev my_db)# task-wait reads the task ID from the JSON the previous command printedacli 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.
Hook scripts around any acli command
Section titled “Hook scripts around any acli command”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.
Non-interactive authentication
Section titled “Non-interactive authentication”acli never prompts for login if credentials are already available. It checks, in priority order:
- An access token in the
ACLI_ACCESS_TOKENenvironment variable, with its correspondingACLI_ACCESS_TOKEN_EXPIRYvalue. - An
ACLI_KEYenvironment variable and its correspondingACLI_SECRET. - Values stored in
~/.acquia/cloud_api.confbyacli 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:
ACLI_KEY=<key> ACLI_SECRET=<secret> \ acli -n api:applications:listBecause 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.
When something goes wrong
Section titled “When something goes wrong”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:
acli self-updateDownloading Acquia CLI (acquia/cli) 4.0.0Download finishedUpdating phar...Successfully updated acliremote: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.
Next steps
Section titled “Next steps”- acli command reference: every command’s flags, defaults, and exit codes.
- Which CLI do I use?:
aclidoesn’t manage Pipelines, Drupal, or Canvas components; this table routes you to the tool that does.
Was this page helpful?
What went wrong?
Still stuck? Contact Acquia Support (opens in a new tab)