Deploy configuration from CI/CD
Goal: import a Source CMS site’s configuration from the CI system you already run, on every push to your main branch.
What you’ll have when you’re done
Section titled “What you’ll have when you’re done”- A pipeline job that imports the committed configuration on every push to your main branch, with no prompts.
- A job that fails when the site refuses the import.
Prerequisites
Section titled “Prerequisites”- A working copy with
.acquia/configcommitted and the site recorded, from Sync configuration with acli. - A Source CMS site you hold site-administrator rights on. Organization administrators hold those rights today; see what the pipeline’s credentials can do before you create the token.
- Cloud Platform API credentials (key and secret). Create them as shown on the Cloud Platform API page.
- A CI system that can run a Linux x86_64 or macOS aarch64 job and hold two secrets.
-
Give the pipeline credentials and a site
Section titled “Give the pipeline credentials and a site”Store a Cloud Platform API key and secret as CI secrets, and expose them as
ACLI_KEYandACLI_SECRET, the namesaclireads in place ofacli auth:login. The examples store them asACQUIA_CLOUD_KEYandACQUIA_CLOUD_SECRET.Supply the site with a committed
.acquia-cli.ymlor--site, as on Sync configuration with acli, and pass--yesto the import. -
Add the job
Section titled “Add the job”Each example installs the native
aclibinary and imports on every push tomain.Only one import runs per site at a time, so a job that starts while the previous one is still importing is refused. Each example first waits for a running import to clear:
acli source:cms:config:push --statusreports the site’s latest import without starting one, and exits2while it is still running. The CI system’s retry handling repeats that check until it exits anything else.GitHub Actions has no built-in retry for one step, so its example uses the community action
nick-fields/retry. Bitbucket retries a step on any failure, so its check runs in a step of its own.name: Push Source configon:push:branches: [main]jobs:push-config:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v4- name: Install Acquia CLIrun: |curl -fsSL -o acli.tar.gz https://github.com/acquia/cli/releases/latest/download/native-acli-linux-x86_64.tar.gztar -xzf acli.tar.gzsudo mv acli /usr/local/bin/acli- name: Wait for any running import to clearuses: nick-fields/retry@v3env:ACLI_KEY: ${{ secrets.ACQUIA_CLOUD_KEY }}ACLI_SECRET: ${{ secrets.ACQUIA_CLOUD_SECRET }}with:max_attempts: 5retry_wait_seconds: 60timeout_minutes: 1retry_on_exit_code: 2 # only retry while an import is running# 2 = running, retry; anything else (including a previous import's own refusal) means proceedcommand: 'acli source:cms:config:push --status --site=my-site-id; [ $? -eq 2 ] && exit 2 || exit 0'- name: Push configurationenv:ACLI_KEY: ${{ secrets.ACQUIA_CLOUD_KEY }}ACLI_SECRET: ${{ secrets.ACQUIA_CLOUD_SECRET }}run: acli source:cms:config:push --site=my-site-id --yespush-config:stage: deployimage: alpine:latestrules:- if: '$CI_COMMIT_BRANCH == "main"'variables:ACLI_KEY: $ACQUIA_CLOUD_KEYACLI_SECRET: $ACQUIA_CLOUD_SECRETretry:max: 5exit_codes: 2 # only retry the job while an import is runningbefore_script:- apk add --no-cache curl- curl -fsSL -o acli.tar.gz https://github.com/acquia/cli/releases/latest/download/native-acli-linux-x86_64.tar.gz- tar -xzf acli.tar.gz && mv acli /usr/local/bin/acliscript:# 2 = running, fail the job so retry.exit_codes retries it; anything else# (including a previous import's own refusal) means proceed- 'acli source:cms:config:push --status --site=my-site-id; [ $? -eq 2 ] && exit 2 || exit 0'- acli source:cms:config:push --site=my-site-id --yespipelines:branches:main:- step:name: Wait for any running import to clearimage: alpine:latestscript:- apk add --no-cache curl- curl -fsSL -o acli.tar.gz https://github.com/acquia/cli/releases/latest/download/native-acli-linux-x86_64.tar.gz- tar -xzf acli.tar.gz && mv acli /usr/local/bin/acli- export ACLI_KEY=$ACQUIA_CLOUD_KEY- export ACLI_SECRET=$ACQUIA_CLOUD_SECRET# 2 = running, fail the step so on-fail.strategy retries it; anything else# (including a previous import's own refusal) means proceed- 'acli source:cms:config:push --status --site=my-site-id; [ $? -eq 2 ] && exit 2 || exit 0'on-fail:strategy: retrymaxRetryCount: 5 # kept in its own step: this retries on ANY step failure, not just exit 2- step:name: Push Source configimage: alpine:latestscript:- apk add --no-cache curl- curl -fsSL -o acli.tar.gz https://github.com/acquia/cli/releases/latest/download/native-acli-linux-x86_64.tar.gz- tar -xzf acli.tar.gz && mv acli /usr/local/bin/acli- export ACLI_KEY=$ACQUIA_CLOUD_KEY- export ACLI_SECRET=$ACQUIA_CLOUD_SECRET- acli source:cms:config:push --site=my-site-id --yesThe native binary is built for Linux x86_64 and macOS aarch64 only. On an ARM Linux or Windows runner, use
acli.pharwith PHP instead. -
Read the outcome in the pipeline
Section titled “Read the outcome in the pipeline”A refused or failed import fails the job. For what each outcome means, see Read the outcome.
What the pipeline’s credentials can do
Section titled “What the pipeline’s credentials can do”Syncing requires site-administrator rights, which today only organization administrators hold. The pipeline’s token can therefore reach every site in the organization, not only the one it syncs. Keep it in your CI system’s secret store, limit who can run the pipeline, and rotate it with your other Acquia credentials.
What just happened
Section titled “What just happened”The job runs the same import as a terminal session. ACLI_KEY and ACLI_SECRET replace acli auth:login, and --yes with a known site replaces every prompt. A refusal appears only in the command’s output, so read the failed job’s log.
When something goes wrong
Section titled “When something goes wrong”A sync is already running for the site.
The second import is refused without touching the site, and nothing is queued. Rerun the job once the running import finishes, or raise the wait step’s retry budget.
For the site’s other refusals, see When something goes wrong on the acli page.
Next steps
Section titled “Next steps”- Deploy from your own CI/CD: the same shape of job for a headless frontend’s build and deploy.
Was this page helpful?
What went wrong?
Still stuck? Contact Acquia Support (opens in a new tab)