# Set up a codebase

**Goal:** your Cloud Platform [application](/start-here/glossary/#application-cloud-platform)'s Git repository contains a Drupal codebase, so the deploy loop has something to deploy.

## What you'll have when you're done

- A Drupal codebase in the application's Acquia-managed repository, laid out the way the platform expects.
- The right next page for your situation: the quickstart for daily work, or the migration guide if data still lives elsewhere.

## Prerequisites

- [acli](/start-here/glossary/#acli) installed and authenticated ([CLI quickstart](/cloud-platform/cli/quickstart/)), and an [SSH key](/start-here/glossary/#ssh-key) registered (`acli ssh-key:create-upload` handles both generation and registration)
- [Composer](https://getcomposer.org/download/), and a PHP whose extensions include what Drupal core requires (`gd` among them; [Drupal's system requirements](https://www.drupal.org/docs/getting-started/system-requirements) has the full list): the scaffold in step 2 shells out to `composer create-project`, which stops partway through without them
- For the `With an AI agent` tabs: an agentic CLI (Claude Code, Cursor, OpenCode, Codex) running in the project, using the acli credentials above. [Acquia Skills](/cloud-platform/cli/acquia-skills/) sharpens how agents map intent onto acli, but the prompts below carry enough detail to work without it.

## Steps

<Steps>

1. ### Pick your starting point

   - **Your team already has code in the application.** There is nothing to set up: go straight to the [quickstart](/cloud-platform/code-workflow/quickstart/).
   - **You have a Drupal site running somewhere else.** That is a full move of code, database, and files rather than a fresh codebase: follow [Migrate an existing site](/cloud-platform/code-workflow/migrate-to-cloud-platform/).
   - **You're starting a new project.** Continue below.

2. ### Scaffold the project

   <Tabs syncKey="method">
     <TabItem label="Yourself">
       ```bash
       acli app:new:local myapp --template=acquia_drupal_recommended
       ```
     </TabItem>
     <TabItem label="With an AI agent">
       ```text
       > Scaffold a new Drupal project named myapp with acli,
       > using the acquia_drupal_recommended template.
       ```

       The agent runs the same `acli app:new:local` command shown on the `Yourself` tab; approve it when prompted.
     </TabItem>
   </Tabs>

   This generates a Drupal project already laid out for Acquia hosting: `docroot/` as the webroot, Composer-managed dependencies (`--template=acquia_drupal_cms` scaffolds Drupal CMS instead). The [code-workflow guide](/cloud-platform/code-workflow/guide/) explains why that layout is the platform's fixed contract.

3. ### Connect the application and push a built artifact

   [Link](/cloud-platform/reference/acli/#applink) the project to your Cloud Platform application, then build and push a deploy [artifact](/start-here/glossary/#build-artifact):

   <Tabs syncKey="method">
     <TabItem label="Yourself">
       ```bash
       acli app:link myapp
       acli push:artifact myapp.dev
       ```
     </TabItem>
     <TabItem label="With an AI agent">
       ```text
       > Link this project to my Cloud Platform application, then
       > build a code artifact with acli and push it to the dev
       > environment.
       ```

       The agent runs the same `acli app:link` and `acli push:artifact` commands shown on the `Yourself` tab; approve them when prompted.
     </TabItem>
   </Tabs>

   A plain `git push` cannot be the first deploy here: [environments](/start-here/glossary/#environment) run built code, and the scaffold's `.gitignore` keeps the built parts out of the repository (`/vendor/`, `/docroot/core`, the contrib directories, even the scaffolded `docroot/index.php`). A bare push would deploy a webroot with no Drupal in it. [`push:artifact`](/cloud-platform/reference/acli/#pushartifact) closes that gap: it runs `composer install`, commits the built result (vendor directories and scaffold files included, even though the source ignores them), and pushes it to the application's repository.

   Pointed at `myapp.dev`, `push:artifact` pushes the artifact to the branch the dev environment has deployed, and pushing to a deployed branch deploys it. `acli app:vcs:info myapp --deployed` prints which branch or tag each environment has deployed, as does the environment card in the Cloud Platform user interface.

4. ### Verify the environment picked it up

   Watch the deploy task appear in the environment's task log, then open the dev environment's URL from its environment card. A freshly deployed artifact serves Drupal's installer, because the codebase (dependencies included) is there but the site isn't installed yet; seeing the installer means code is flowing.

5. ### Install locally, then push the database up

   Install the site on your machine, not on the environment. A remote `site:install` would generate a site whose configuration has a fresh UUID unconnected to your repository, and the first `drush config:import` after that fails on the mismatch. Installing locally keeps the site's configuration and the codebase the same lineage from the start.

   Bring the scaffold up locally first (the [local development quickstart](/cloud-platform/local-dev/quickstart/) installs DDEV in step 1 and configures the project in step 3), install Drupal, and export its configuration into the repository:

   ```bash
   ddev start
   ddev drush site:install --yes \
     --account-name=admin --account-pass=CHOOSE_A_PASSWORD
   ddev drush config:export --yes
   git add config && git commit -m "Add initial configuration"
   acli push:artifact myapp.dev
   ```

   Then copy the installed database to the environment:

   <Tabs syncKey="method">
     <TabItem label="Yourself">
       ```bash
       acli push:database myapp.dev
       ```
     </TabItem>
     <TabItem label="With an AI agent">
       ```text
       > Push my local database to the dev environment with acli.
       ```

       The agent runs the same `acli push:database` command shown on the `Yourself` tab; approve it when prompted.
     </TabItem>
   </Tabs>

   `push:database` overwrites the target environment's database, so it is safe here (the environment has no site yet) and needs care later. Reload the environment URL and the site answers instead of the installer.

</Steps>

## When something goes wrong

**`Permission denied (publickey)` on push**: your SSH key isn't registered with Acquia, or the wrong key is offered. `acli ssh-key:create-upload` generates and registers one; the [quickstart's troubleshooting entry](/cloud-platform/code-workflow/quickstart/#when-something-goes-wrong) shows how to see which keys were tried.

**The push succeeds but nothing deploys**: the artifact went to a branch no environment tracks. `acli push:artifact myapp.dev` targets the branch the dev environment has deployed, so this usually follows a push with `--destination-git-branch` to a new branch name. Compare that branch name against `acli app:vcs:info myapp --deployed` (or the environment card), and point the environment at it with `acli api:environments:code-switch myapp.dev my-branch --task-wait`, the command behind `Switch code` on the card.

**No installer, and nothing Drupal answers, after deploying with plain `git push`**: a plain push deploys only what git tracks, and the scaffold's `.gitignore` excludes `/vendor/`, `/docroot/core`, and the scaffolded `docroot/index.php`, so the environment received a webroot with no Drupal entry point. Deploy with `acli push:artifact myapp.dev` (step 3) and reload.

**`remote:drush` fails with `pcntl_exec(): ... Permission denied`**: the environment restricts PHP process execution, so the Drush launcher can't hand off. Run `drush.php` through PHP over SSH instead, as [running Drush remotely](/cloud-platform/cli/everyday-workflows/#run-drush-remotely-or-open-a-shell) shows.

## Next steps

- [Quickstart: ship a change](/cloud-platform/code-workflow/quickstart/): run the full deploy loop once, end to end.
- [Code-workflow guide](/cloud-platform/code-workflow/guide/): the layout contract, branches and tags, moving data, remote drush.
- [Manage configuration](/cloud-platform/code-workflow/configuration/): where the `config:export` from step 5 belongs in the repository, and how it gets imported on every later deploy.
- [Set up local development](/cloud-platform/local-dev/quickstart/): run the new site on your machine.
