> ## Documentation Index
> Fetch the complete documentation index at: https://astronomer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Adopt Astro Runtime Operator managed Deployments (Preview)

If you already run Airflow with the Astro Runtime Operator, you can bring those Deployments under Astro Private Cloud (APC) management by **adopting** them. Your Airflow keeps running. Nothing is recreated, and the operator continues to reconcile the underlying resources. After adoption, you manage the Deployment from the APC control plane like any other: deploy code, change environment variables, resize components, and view metrics and logs.

<Note>
  **Astro Private Cloud 2.1**

  This feature was introduced in Astro Private Cloud 2.1. To access this feature, upgrade your Astro Private Cloud installation to 2.1 or later.
</Note>

<Info>
  **Preview**

  Adoption is in Preview because APC does not yet manage everything the Astro Runtime Operator can express. An adopted Deployment keeps some of its settings under the operator rather than under APC, and operator mode itself is still missing some Helm-mode features. See [What adoption changes](#what-adoption-changes) for the ownership split, and [Feature support](/docs/astro-private-cloud/v-2-x/airflow-operator-mode#feature-support) for what operator mode does not cover yet.
</Info>

Adoption is the second stage of a two-stage path onto the platform:

* **Stage 1**: you run Airflow with the Astro Runtime Operator on your own Kubernetes cluster. Each Airflow Deployment is defined by one Airflow custom resource, and the operator turns that resource into the running Kubernetes workloads.
* **Stage 2**: you register that cluster as an APC data plane and adopt its Airflow Deployments. APC takes ownership of a defined set of settings, and the operator continues to own the rest.

Adoption leaves the operator itself alone: you keep installing and upgrading it. If you would rather APC took that over too, see [Move the operator under APC](/docs/astro-private-cloud/v-2-x/transition-operator-to-apc).

How the pieces fit together, since the terms are easy to mix up:

* The operator's **Custom Resource Definition (CRD)** is installed once on the cluster. It only defines the shape of an Airflow resource; it doesn't hold any Deployment's configuration.
* Each of your Airflow Deployments is one **Airflow custom resource**, created against that definition. It holds that Deployment's configuration.
* The **operator** watches those custom resources and builds the real Kubernetes objects from them: schedulers, workers, services, and the rest.

Adoption doesn't rearrange any of that. APC writes directly to an individual custom resource, and the operator reconciles the change exactly as it would if you had edited the resource yourself. Nothing is written to the CRD, and APC never replaces the operator.

```mermaid theme={null}
flowchart TD
  crd["Airflow CRD: installed once per cluster, defines the shape only"]
  apc["APC control plane"]
  you["Your pipeline, Helm chart, or kubectl"]
  cr["Airflow custom resource: one per Deployment, holds its configuration"]
  operator["Astro Runtime Operator"]
  workloads["Scheduler, workers, API server or webserver, triggerer"]

  crd -.->|defines the shape of| cr
  apc -->|writes only the fields it owns| cr
  you -->|writes everything else| cr
  cr -->|watched by| operator
  operator -->|creates and reconciles| workloads
```

Both writers act on the same custom resource, so which fields each one owns is the thing to understand before you adopt: see [What adoption changes](#what-adoption-changes). If you stop touching the resource yourself after adoption, the second arrow simply goes away; if you keep managing it, read [Keep your own pipeline and APC from fighting](#keep-your-own-pipeline-and-apc-from-fighting).

When you adopt a Deployment, APC applies the configuration it owns to that Deployment's Airflow custom resource, so expect the Deployment's pods to restart once shortly after you adopt. Read [What adoption changes](#what-adoption-changes) before you begin so you know what APC takes over.

## What adoption changes

When APC adopts an Airflow Deployment, it takes ownership of a specific set of fields and leaves everything else to you and the operator. In this release, nothing outside the "APC takes over" column is modified, either at adoption or on any later update.

Ownership is split rather than transferred wholesale because your custom resource can express things APC has no equivalent for: more than one worker queue, KEDA autoscaling, per-component pod templates, sidecars. APC claims only the fields it needs in order to manage the Deployment, which is what image it runs, which executor, the web component it puts authentication in front of, and the labels its monitoring and log shipping key on. If it claimed the rest, every update would have to overwrite your configuration with APC's narrower model. Leaving those fields alone is what makes adoption non-destructive. The trade-off is that they stay managed where they are today, through the operator, rather than through APC.

This is where the line falls today, not a permanent boundary. APC does not yet cover everything the Astro Runtime Operator can express, and the set of settings it manages is expected to widen in future releases. Operator mode has its own gaps against Helm mode, listed in [Feature support](/docs/astro-private-cloud/v-2-x/airflow-operator-mode#feature-support). Check this page against the version of APC you are running rather than assuming the split is fixed.

| APC takes over                                                                                                                                                                                                       | Stays yours                                                                                                                                                                                                                            |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Airflow image and Astro Runtime version. APC owns these fields from adoption, but seeds them from your existing custom resource, so the Deployment keeps running the image it already had until you deploy new code. | Sizing and replica counts for every component except the webserver or API server, including the scheduler, workers, triggerer, and Dag processor                                                                                       |
| Executor selection (see the warning under [Worker queues and autoscaling](#worker-queues-and-autoscaling))                                                                                                           | Your `airflow.cfg` and any config you set through it                                                                                                                                                                                   |
| The webserver (Airflow 2) or API server (Airflow 3) component **in full**, including its ingress, authentication, resources, and replicas                                                                            | Pod template overrides on every other component, including custom volumes, sidecars, tolerations, and node selectors. APC adds its own labels to those pod templates for attribution and log routing, but changes nothing else in them |
| Environment variables you set through APC                                                                                                                                                                            | Environment variables referencing your own Secrets or ConfigMaps                                                                                                                                                                       |
| Metrics exporter labels and the network policy rules needed to scrape them                                                                                                                                           | Your metadata database, its connection Secrets, and its credentials                                                                                                                                                                    |
| Nothing in the worker section                                                                                                                                                                                        | Every worker queue and its KEDA autoscaling, including queues beyond the first                                                                                                                                                         |
| Task logging destination and image pull Secret (**only if you opt in**)                                                                                                                                              | Task logging and image registry configuration if you do not opt in                                                                                                                                                                     |

<Warning>
  **APC takes over Airflow web access.** APC applies its own authentication and ingress to the webserver or API server component on every adopted Deployment. If your Airflow currently authenticates users through LDAP, a `REMOTE_USER` proxy, or a custom `webserver_config.py`, that configuration is replaced with APC sign-in the first time APC applies its configuration.

  This is why [giving the Deployment's users APC accounts](#step-4-give-the-deployments-users-apc-accounts) is a required step and not an optional one. Everyone who signs in to this Airflow today needs a matching APC account, and anyone who exists only in Airflow loses access until they have one.
</Warning>

Two more things before you start:

* **Component resources are brought into your platform's supported range.** If a component in your custom resource requests less than your platform's minimum or more than its maximum, APC adjusts it to the nearest supported value at adoption. See [Configure component size limits](/docs/astro-private-cloud/v-2-x/configure-component-size-limits).
* **Adoption is not a migration of history.** Existing task logs stay wherever they are today. If you switch task logging to APC, only logs written after the switch are readable from the Airflow UI; older ones remain in your own store but the Airflow UI no longer resolves them.

<Warning>
  **Don't adopt a Deployment whose image is pinned by digest.** If your custom resource references its image by digest (`myrepo/airflow@sha256:...`) rather than by tag, adoption rewrites it to a tag reference built from the Deployment's Astro Runtime version (`myrepo/airflow:<runtime-version>`). The digest pin is lost, and if that tag doesn't exist in your repository the Deployment stops being able to pull its image.

  This happens on the first apply, before you deploy anything. Re-tag the image and update the custom resource to reference it by tag before adopting, or hold off on adopting that Deployment. Astronomer is addressing this.
</Warning>

## Prerequisites

* Airflow Deployments running under the Astro Runtime Operator, on a cluster where operator support is enabled. See [Airflow Operator mode](/docs/astro-private-cloud/v-2-x/airflow-operator-mode).
* The operator's cluster registered as an APC data plane. See [Install a data plane cluster](/docs/astro-private-cloud/v-2-x/install-data-plane) and [Register a data plane cluster](/docs/astro-private-cloud/v-2-x/register-data-plane).
* Operator support and adoption enabled on your platform. See [Enable adoption on your platform](#enable-adoption-on-your-platform).
* Permission to adopt. Two permissions are involved: `workspace.deployments.adopt` to adopt a Deployment into a Workspace, and `system.deployments.adopt` to browse adoption candidates, which is separate because listing candidates scans a whole cluster. Among the built-in roles, **Workspace Admin** carries the first and **System Admin** the second. **Cluster Admin does not carry either**, because it governs cluster configuration rather than Deployments. If your platform uses custom roles, both permissions can be granted to one. See [Manage permissions](/docs/astro-private-cloud/v-2-x/manage-permissions) and the [role and permission reference](/docs/astro-private-cloud/v-2-x/role-permission-reference).
* No existing APC Deployment using the custom resource's name, or the namespace it runs in. Adoption is rejected if either is already taken.
* The custom resource references its image **by tag, not by digest**. See the warning under [What adoption changes](#what-adoption-changes).
* To use the Astro CLI instead of the UI, APC **2.1.0 or later** and a matching Astro CLI. See [Install the Astro CLI](https://www.astronomer.io/docs/astro/cli/install-cli).

## Enable adoption on your platform

Adoption builds on operator support, so both have to be on.

**Operator support** is a prerequisite and is configured separately, including its webhook TLS certificate. Follow [Enable operator support](/docs/astro-private-cloud/v-2-x/airflow-operator-mode#enable-operator-support) first if it isn't on yet.

**Adoption** is then controlled by one additional value, which is already on by default:

| Value                                     | Default | What it does                                                                                                                     |
| ----------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `global.airflowOperator.enabled`          | `false` | Turns on operator support. Required for adoption. See [Airflow Operator mode](/docs/astro-private-cloud/v-2-x/airflow-operator-mode). |
| `global.airflowOperator.adoption.enabled` | `true`  | Allows APC to adopt Deployments that already exist on the cluster.                                                               |

<Note>
  `global.airflowOperator.adoption.enabled` is already `true`, but it does nothing on its own. Adoption is enabled only when **both** values are `true`, so on a default install you enable adoption by turning on operator support. Leave the adoption value alone unless you specifically want operator support without adoption, in which case set it to `false`.
</Note>

Both values are read on the control plane and on the data plane, for different things: the control plane uses them to accept the adoption operations, and the data plane uses them to grant APC access to the Airflow custom resources and to scrape operator-managed Deployments. Set them wherever you set operator support.

### Don't install a second operator

Your cluster already runs the Astro Runtime Operator, so tell the platform chart not to install its own alongside it:

```yaml theme={null}
global:
  airflowOperator:
    enabled: true
    adoption:
      enabled: true

airflow-operator:
  # Skip installing APC's own operator; this cluster already runs one.
  enabled: false
```

<Warning>
  Set `airflow-operator.enabled: false` but keep `global.airflowOperator.enabled: true`. Setting the global value to `false` to avoid installing a second operator also switches off the permissions and the API operations adoption depends on, so adoption stops working entirely.
</Warning>

Add the values to the platform configuration file you install with, then upgrade the release. See [Apply platform configuration](/docs/astro-private-cloud/v-2-x/apply-platform-config). Enabling this does not change Deployments that already exist; it adds the adoption capability.

### Confirm it's enabled

Sign in to the control plane as a System Admin and look for **System** > **Adoption Candidates (Preview)**. If the section is there, adoption is enabled. If it's missing, or an adopt call reports that operator adoption is disabled, one of the two values is still `false` on the control plane.

## Step 1: Decide how to handle logging, images, and metrics

Adoption asks you to make two choices, logging and images. Both are set when you adopt and are not intended to be changed afterwards, so decide before you start. Metrics need no decision: they are always on.

### Task logging

Choose whether APC becomes the destination for your task logs.

* **Route logs to APC.** APC configures Airflow to write task logs to APC's configured log store, and the Airflow UI reads them back from there. This **overrides your Deployment's existing remote logging**. If your tasks currently log to Amazon S3, Google Cloud Storage, or your own Elasticsearch, they log to APC instead from then on. Logs written before the switch stay where they are, and the Airflow UI no longer resolves them.
* **Keep your own logging.** APC changes nothing about logging. Your tasks keep logging where they do prior to adoption, not in APC's configured log store.

See [Configure logging](/docs/astro-private-cloud/v-2-x/logs-configuration), [Export task logs](/docs/astro-private-cloud/v-2-x/export-task-logs), and [Send logs to S3](/docs/astro-private-cloud/v-2-x/logs-to-s3).

### Image registry

Two separate settings decide where your Deployment's image comes from. Don't confuse them.

**Your platform's registry** is configured once, for every Deployment on the platform, adopted or not. By default that's APC's built-in registry. To use your own instead, configure a custom image registry before you adopt: see [Use a custom image registry](/docs/astro-private-cloud/v-2-x/custom-image-registry) and [Registry backend](/docs/astro-private-cloud/v-2-x/registry-backend). APC synchronizes that registry's credential into every Deployment namespace, including adopted ones.

**The adoption choice** is narrower. It decides whether APC manages *this Deployment's* image reference and pull credential:

* **Use APC's registry** (`--use-apc-registry`). APC takes over the Deployment's image and provisions the pull credential its pods need. You don't have to move the image yourself first: adoption leaves the Deployment on the image it already runs, and the switch to your platform's registry happens on your first `astro deploy`, which moves the image and the pull credential together. Deploy code with `astro deploy` or a CI/CD pipeline as normal. See [Deploy code overview](/docs/astro-private-cloud/v-2-x/deploy-code-overview) and [CI/CD](/docs/astro-private-cloud/v-2-x/ci-cd).
* **Keep your own** (the default). APC leaves the Deployment's image and pull Secret exactly as they are and never manages them. Use this when something outside APC builds and pushes the image.

#### Deploy code when you keep your own image

You can still ship new code through APC. Build and push the image to your own registry, then point the Deployment at it:

```bash theme={null}
astro deploy --remote --image-name=<your-registry>/<repository>:<tag> --runtime-version=<runtime-version> <deployment-id>
```

APC updates the Deployment to run that image without touching your registry or your pull credential. `--runtime-version` is required with `--remote`. Your platform administrator must have set `deployments.enableUpdateDeploymentImageEndpoint: true`, which the [custom image registry](/docs/astro-private-cloud/v-2-x/custom-image-registry) setup already covers.

<Warning>
  **Don't run plain `astro deploy` on a Deployment that kept its own image.** Without `--remote`, `astro deploy` builds your project and pushes it to APC's built-in registry, then repoints the Deployment at it. Because you opted out, APC never provisioned a credential for that registry, so the Deployment's pods fail to pull the new image and stop starting. The command reports success, and the previous working image reference is gone.

  Use `--remote --image-name` as shown above, or adopt with `--use-apc-registry` if you want APC to own the Deployment's image.
</Warning>

<Note>
  The adoption choice is fixed at adoption. To change it later, release the Deployment and adopt it again with the setting you want.
</Note>

<Note>
  If APC detects that your custom resource already points at this cluster's own log store or image registry, for example because the Deployment was previously managed by a different control plane, it takes ownership of that wiring regardless of what you choose here. Leaving it half-owned would break the Deployment.
</Note>

### Metrics

Metrics are always enabled and have no option. APC labels the Deployment's metrics exporters so its monitoring stack collects them, which is additive and changes nothing about how your Airflow runs. If you collect metrics with your own Prometheus, keep doing so. APC's collection does not interfere. See [Deployment metrics](/docs/astro-private-cloud/v-2-x/deployment-metrics) and [Configure metrics](/docs/astro-private-cloud/v-2-x/configure-metrics).

### Settings APC can't fully represent

Some custom resource settings have no exact equivalent in APC. Examples include an environment variable set to different values on different components, and more than one worker group. By default, adoption proceeds and records these as partially represented, leaving the underlying setting in place and working. You can instead require a clean match, in which case adoption fails and reports what didn't fit rather than adopting. Use that mode when you want to review the differences first.

## Step 2: Review adoption candidates

An adoption candidate is an operator-managed Airflow custom resource on a registered data plane that no APC Deployment claims yet.

<Tabs>
  <Tab title="Astro UI">
    1. In the control plane, go to **System** > **Adoption Candidates (Preview)**.
    2. Select the data plane cluster from the dropdown. Candidates appear only after you pick a cluster.
    3. Review the candidates. Each shows the custom resource's name, its Kubernetes namespace, and the Astro Runtime and Airflow versions read from the resource.

    <Frame>
      <img src="https://mintcdn.com/astronomer/V_9YWWO4aJDSUYR5/images/astro-private-cloud/adopt_deployment_list.png?fit=max&auto=format&n=V_9YWWO4aJDSUYR5&q=85&s=7137ceffc949830b08203d60ec6d1834" alt="The Adoption Candidates page with a cluster selected, listing one candidate with an Adopt button." width="3454" height="906" data-path="images/astro-private-cloud/adopt_deployment_list.png" />
    </Frame>
  </Tab>

  <Tab title="APC API">
    ```graphql theme={null}
    query {
      adoptionCandidates(clusterId: "<data-plane-cluster-id>") {
        crName
        crNamespace
        runtimeVersion
        airflowVersion
      }
    }
    ```
  </Tab>
</Tabs>

There is no Astro CLI command for listing adoption candidates. Use the Astro UI or the APC API.

If a Deployment you expected doesn't appear, it is usually because an APC Deployment already uses that custom resource's name, or the namespace it runs in, or because the cluster you selected isn't the one it runs on.

## Step 3: Adopt the Deployment

Adopting is a single operation, available from all three surfaces. Pick one.

<Tabs>
  <Tab title="Astro UI">
    1. Go to **System** > **Adoption Candidates (Preview)**.
    2. Select the data plane cluster from the dropdown. The candidates on that cluster then appear. Until you pick one, the page prompts you to select a cluster and shows no candidates.
    3. Select **Adopt** on the candidate you want. The adoption drawer opens, showing the custom resource and the namespace and cluster it runs on.
    4. Choose the **Workspace** to adopt the Deployment into. This is the only required field.
    5. Optionally set a **Label** and **Description**. The label defaults to the custom resource name when left blank.
    6. Set the options you decided on in [Step 1](#step-1-decide-how-to-handle-logging-images-and-metrics):
       * **Route logs to APC (Elasticsearch / Vector)**
       * **Use APC's image registry**
       * **Adopt even if some settings can't be mapped to APC**, which is selected by default. Clear it to require a clean match.
    7. Select **Adopt Deployment**.

    <Frame>
      <img src="https://mintcdn.com/astronomer/V_9YWWO4aJDSUYR5/images/astro-private-cloud/adopt_deployment_drawer.png?fit=max&auto=format&n=V_9YWWO4aJDSUYR5&q=85&s=70bb235885022a57bf2023ea869ee33e" alt="The adoption drawer for a candidate, showing the custom resource, Workspace picker, label, description, and the three adoption options." width="420" data-path="images/astro-private-cloud/adopt_deployment_drawer.png" />
    </Frame>
  </Tab>

  <Tab title="APC API">
    ```graphql theme={null}
    mutation {
      adoptDeployment(
        workspaceUuid: "<workspace-id>"
        clusterId: "<data-plane-cluster-id>"
        crNamespace: "<airflow-cr-namespace>"
        crName: "<airflow-cr-name>"
        label: "My adopted deployment"
        useApcLogging: false
        useApcRegistry: false
        acceptIncompatibilities: true
      ) {
        id
        releaseName
        namespace
        isAdopted
        adoptedAt
      }
    }
    ```

    `workspaceUuid`, `clusterId`, `crNamespace`, and `crName` are required. `label` defaults to the custom resource name, `useApcLogging` and `useApcRegistry` default to `false`, and `acceptIncompatibilities` defaults to `true`.
  </Tab>

  <Tab title="Astro CLI">
    The Deployment is adopted into your **currently selected Workspace**. Switch Workspaces first if you need a different one:

    ```bash theme={null}
    astro workspace switch
    ```

    Then adopt:

    ```bash theme={null}
    astro deployment adopt --cluster-id=<cluster-id> --name=<cr-name> --namespace=<cr-namespace>
    ```

    `--cluster-id`, `--name`, and `--namespace` are required. Add any of the following:

    | Flag                         | Description                                                                                                                              |
    | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
    | `--label`, `-l`              | Display label. Defaults to the custom resource name.                                                                                     |
    | `--description`              | Longer description for the Deployment.                                                                                                   |
    | `--use-apc-logging`          | Route task logs to APC. Off by default.                                                                                                  |
    | `--use-apc-registry`         | Pull images from APC's registry. Off by default.                                                                                         |
    | `--accept-incompatibilities` | Adopt even when some fields have no APC representation. On by default; pass `--accept-incompatibilities=false` to require a clean match. |
  </Tab>
</Tabs>

### What happens when you adopt

Each Airflow Deployment has one custom resource, and you adopt them one at a time. Whichever surface you use, APC then:

1. Reads the live Airflow custom resource from the cluster and maps it onto an APC Deployment.
2. Creates the Deployment in your Workspace and grants you Deployment Admin on it.
3. Applies its managed configuration to the running custom resource. **The Deployment's pods restart once during this step.**

Your Airflow is not recreated, its namespace is not changed, and its metadata database is left exactly as it is.

## Step 4: Give the Deployment's users APC accounts

**Every person who uses this Airflow needs their own APC account.** After adoption, APC's authentication guards the Airflow UI, and an APC account is the only way in. People reach Airflow through the token APC mints for their APC role, not through the Airflow account they signed in with before.

This step exists because the Deployment is adopted rather than created. A Deployment you create in APC starts with no users and gains them as you invite people. An adopted Deployment arrives with a user population that has been signing in all along, so there is an existing set of Airflow users to reproduce one-for-one in APC. Anyone you don't reproduce loses access at cutover.

Where that roster comes from depends on how the Airflow you adopted authenticated people. APC asks the running Airflow which auth manager it uses:

* **Flask-AppBuilder (FAB), which is the Airflow 2 default.** Airflow keeps its own user table, so APC reads it and offers you the roster. Follow the rest of this step.
* **Airflow 3.** APC sets Airflow 3's auth manager to its own at adoption, and there is no FAB user table behind it, so there is no roster to read. APC tells you this explicitly rather than showing an empty list. [Invite the users to APC](/docs/astro-private-cloud/v-2-x/manage-permissions#invite-users) instead, and skip the rest of this step.

### Import users from Airflow 2

<Tabs>
  <Tab title="Astro UI">
    1. Open the adopted Deployment and go to the **Import Users** tab. It appears only on adopted Deployments.
    2. Review the discovered users. For each, APC suggests a Workspace role and a Deployment role based on the user's Airflow role.
    3. Adjust the roles and clear the checkbox next to anyone you don't want to import.
    4. If your platform uses an identity provider, select **Skip invite email**. Those users sign in through your IdP, so they never need to set a password.
    5. Select **Import selected users**.

    <Frame>
      <img src="https://mintcdn.com/astronomer/V_9YWWO4aJDSUYR5/images/astro-private-cloud/adopted_deployments_import_users.png?fit=max&auto=format&n=V_9YWWO4aJDSUYR5&q=85&s=2f19915060ca908e71c3278f89783c29" alt="The Import Users tab on an adopted Deployment, listing discovered Airflow users with their Airflow roles and a suggested Workspace and Deployment role for each." width="3446" height="1730" data-path="images/astro-private-cloud/adopted_deployments_import_users.png" />
    </Frame>
  </Tab>

  <Tab title="APC API">
    Discover the users:

    ```graphql theme={null}
    query {
      adoptedAirflowUsers(deploymentId: "<deployment-id>") {
        username
        email
        fullName
        active
        fabRoles
        suggestedWorkspaceRole
        suggestedDeploymentRole
        alreadyImported
      }
    }
    ```

    Import the ones you want:

    ```graphql theme={null}
    mutation {
      deploymentUserBulkImport(
        deploymentId: "<deployment-id>"
        bypassInvite: false
        users: [
          { email: "user@example.com", fullName: "Example User", workspaceRole: WORKSPACE_VIEWER, deploymentRole: DEPLOYMENT_EDITOR }
        ]
      ) {
        email
        status
        created
        rolesAssigned
        inviteToken
        error
      }
    }
    ```

    Each row succeeds or fails independently, so a single bad address doesn't fail the whole import.

    Set `bypassInvite: true` for an IdP-backed platform to skip the invitation email, the same as **Skip invite email** in the Astro UI.
  </Tab>
</Tabs>

There is no Astro CLI command for importing users. Use the Astro UI or the APC API.

### How roles are mapped

How Airflow roles are suggested, and what the imported roles mean in Airflow:

| Airflow role found                     | Suggested Deployment role                                                | Resulting Airflow access    |
| -------------------------------------- | ------------------------------------------------------------------------ | --------------------------- |
| `Admin`                                | Deployment Admin                                                         | Airflow Admin               |
| `User`                                 | Deployment Editor                                                        | Airflow User                |
| `Viewer`                               | Deployment Viewer                                                        | Airflow Viewer              |
| Any other role, including custom roles | None. The dropdown shows **View only (no elevation)** until you pick one | Follows the role you choose |

Every imported user gets Workspace membership as well, because a Deployment role requires it. Users who already have access in this Workspace are marked as already imported and are skipped.

The Airflow role APC found is only used to suggest a role. Importing someone carries none of their old Airflow permissions across, so what they can do in Airflow afterwards comes entirely from the APC Deployment role you give them. Check the suggestions rather than accepting them wholesale, particularly for anyone who held a custom Airflow role.

<Note>
  **Discovery reads Airflow's own FAB user table.** If the Deployment doesn't have one, which is the case on an adopted Airflow 3, discovery returns an error saying no FAB user table was found rather than an empty list. That is expected, not a failed adoption. [Invite those users to APC](/docs/astro-private-cloud/v-2-x/manage-permissions#invite-users) instead.
</Note>

<Note>
  Imported users are created as pending invitations with no password. They set their own password by completing the invitation, or sign in directly if your platform uses an identity provider. If your platform can't send email, the import result returns each user's invitation token so you can deliver it yourself. See [Integrate an auth system](/docs/astro-private-cloud/v-2-x/integrate-auth-system) and [Import IdP groups](/docs/astro-private-cloud/v-2-x/import-idp-groups).
</Note>

## Step 5: Verify the adoption

1. The Deployment appears in your Workspace and reports healthy.
2. Your Airflow is still serving, and its Dags and history are intact.
3. Sign in to the Airflow UI as an imported user and confirm the expected role.
4. Change something in APC and confirm it reaches the running Airflow. Adding an environment variable is the simplest check. See [Environment variables](/docs/astro-private-cloud/v-2-x/environment-variables).
5. If you routed logs to APC, run a task and confirm its logs appear in the Airflow UI.
6. Confirm the Deployment's metrics are populating. See [Deployment metrics](/docs/astro-private-cloud/v-2-x/deployment-metrics).

## Manage an adopted Deployment

An adopted Deployment behaves like any other APC Deployment for everything APC owns:

* **Deploy code**: [Deploy code overview](/docs/astro-private-cloud/v-2-x/deploy-code-overview), [Deploy Dags](/docs/astro-private-cloud/v-2-x/deploy-dags), [CI/CD](/docs/astro-private-cloud/v-2-x/ci-cd). If the Deployment kept its own image, deploy with `--remote --image-name` instead: see [Deploy code when you keep your own image](#deploy-code-when-you-keep-your-own-image).
* **Environment variables**: [Environment variables](/docs/astro-private-cloud/v-2-x/environment-variables). Variables that were set per-component in your custom resource are shown read-only, because APC applies variables to all components uniformly. Variables that reference your own Secrets or ConfigMaps are not shown and keep working untouched.
* **Executor**: [Kubernetes executor](/docs/astro-private-cloud/v-2-x/kubernetes-executor). APC applies the change and the operator adjusts the supporting components.
* **Resources**: [Scale Deployment resources](/docs/astro-private-cloud/v-2-x/scale-deployment-resources), [Configure component size limits](/docs/astro-private-cloud/v-2-x/configure-component-size-limits). Webserver or API server sizing is set through APC; scheduler, worker, and triggerer sizing stays with your custom resource. Worker settings in particular are not applicable, see [Worker queues and autoscaling](#worker-queues-and-autoscaling).
* **Runtime upgrades**: [Migrate to Airflow 3](/docs/astro-private-cloud/v-2-x/migrate-to-airflow-3).
* **Secrets backends**: [Secrets backend](/docs/astro-private-cloud/v-2-x/secrets-backend). Unchanged by adoption; these are ordinary environment variables to APC.

## Worker queues and autoscaling

APC models a single worker queue per Deployment and does not use KEDA autoscaling for operator-based Deployments. Many operator-managed Deployments use more than one worker queue, KEDA autoscaling, or both.

Adoption handles this by leaving the worker section alone entirely. APC never writes it on an adopted Deployment, so:

* **Your worker queues keep running unchanged**, including every queue beyond the first. APC does not collapse them into one, rename them, or add a queue of its own.
* **Your KEDA autoscaling keeps running unchanged.** APC does not disable it, even though APC's own operator-based Deployments don't use it.
* **Extra worker queues are recorded as partially represented** at adoption. APC's own view of the Deployment shows the first queue; the others are stored but not surfaced as editable.

The trade-off is that worker settings in APC don't reach an adopted Deployment:

<Warning>
  **Changing worker count, worker resources, or autoscaling in APC has no effect on an adopted Deployment.** APC accepts the change and stores it, and the Deployment's own view shows the new value, but the running Airflow keeps the worker configuration it already had. There is no error and no warning. Manage worker sizing and autoscaling through your operator configuration instead, and treat APC's worker settings as not applicable for these Deployments.
</Warning>

<Warning>
  **Changing the executor on an adopted Deployment discards your worker queues and autoscaling.** The executor *is* APC-managed, and the operator rebuilds worker topology from whatever the executor implies: switching to KubernetesExecutor removes the worker queues entirely, and switching to CeleryExecutor replaces them with a single default queue. Extra queues and KEDA configuration do not survive either change. If your Deployment relies on multiple worker queues, do **not** change its executor from APC.
</Warning>

If you need APC to manage worker sizing and autoscaling for these Deployments, keep them on the operator for now rather than adopting them.

## Data plane failover

**Adopted Deployments are not covered by data plane failover.** APC's failover recreates the Deployments it created on a target cluster; it has no path for a Deployment whose definition lives in a custom resource on the original cluster, so adopted Deployments are not brought up on the failover target.

<Warning>
  Nothing currently stops you from initiating failover for a data plane that has adopted Deployments. Failover starts, and the adopted Deployments simply do not arrive on the target cluster. Do **not** count failover as disaster recovery for an adopted Deployment.

  If you run adopted Deployments on a data plane that has failover enabled, plan their recovery separately: keep the Airflow custom resource and its supporting configuration in source control, and be ready to apply it to the target cluster and adopt it again there.
</Warning>

See [Data plane failover](/docs/astro-private-cloud/v-2-x/data-plane-failover) and [Enable data plane failover](/docs/astro-private-cloud/v-2-x/enable-data-plane-failover) for how failover works for Deployments APC created.

## Keep your own pipeline and APC from fighting

After adoption, two things write to the same Airflow custom resource: APC, and whatever you use to manage the resource yourself, such as a GitOps controller, a Helm chart, or `kubectl` in a CI job. If you still patch or upgrade the Deployment through your own pipeline, read this section. If you manage the Deployment only through APC after adoption, you can skip it.

APC writes only the fields listed in [What adoption changes](#what-adoption-changes). It writes them with Kubernetes server-side apply, under the field manager `houston`, and it force-claims them, so a Deployment update always wins over whatever wrote those fields last.

What happens when you upgrade the resource yourself depends entirely on how your tooling writes it:

```mermaid theme={null}
flowchart TD
  start["You upgrade the custom resource from your own pipeline"]
  scoped["Server-side apply, with APC-owned fields removed from your manifest"]
  full["Client-side apply, kubectl replace, or delete and recreate"]
  forced["Server-side apply with force-conflicts, APC-owned fields still in your manifest"]
  ok["APC fields untouched. Nothing to do"]
  wiped["APC fields wiped from the resource. Airflow keeps running without them"]
  flap["Your tooling and APC revert each other on every reconcile"]
  resync["Trigger a Deployment update to restore the APC fields"]
  strip["Remove the APC-owned fields from your manifest, then trigger a Deployment update"]

  start --> scoped
  start --> full
  start --> forced
  scoped --> ok
  full --> wiped
  wiped --> resync
  forced --> flap
  flap --> strip
```

The middle path is the one to watch: your upgrade succeeds, Airflow keeps running, and nothing reports a problem, but the Deployment is now missing the configuration APC applied, including the authentication on its web component. Only a Deployment update puts it back.

### Use server-side apply, and remove APC-owned fields from your manifest

Both halves are necessary. Doing only the first makes things worse, not better.

1. **Apply with server-side apply** so your write only touches the fields your manifest actually declares:

   ```bash theme={null}
   kubectl apply --server-side -f airflow-cr.yaml
   ```

   In Argo CD, set `ServerSideApply=true`. Flux's kustomize-controller already uses server-side apply.

2. **Delete the APC-owned fields from the manifest you apply.** Your manifest is usually the custom resource as it looked before adoption, so it still declares fields APC now owns, such as `spec.image`, `spec.runtimeVersion`, `spec.executor`, and the whole `spec.webserver` or `spec.apiserver` block.

<Warning>
  **Server-side apply on its own turns a one-time problem into a permanent one.** Argo CD's `ServerSideApply=true` runs with `--force-conflicts`, and Flux corrects drift the same way. If your manifest still declares APC-owned fields, your tool force-claims them back, the next Deployment update force-claims them again, and the two keep reverting each other indefinitely. Removing those fields from the manifest is what stops the loop. If your tool supports drift-ignore rules, exclude the APC-owned paths instead.
</Warning>

### Trigger a Deployment update after any full-object write

Client-side `kubectl apply`, `kubectl replace`, and deleting and recreating the custom resource all write the whole object, so they wipe APC's fields in one pass. Your Airflow keeps running, but it now runs without the configuration APC applied, including the authentication on its web component.

Every field APC owns is restored by the next Deployment update, because APC re-applies its full set of managed fields each time and force-claims them. After any write of that kind, and after any pipeline upgrade or patch that you're not certain was field-scoped, trigger a Deployment update. This is the Deployment-level re-sync, not an APC platform upgrade. Nothing needs to change for it to do its job, so a no-op update is enough:

<Tabs>
  <Tab title="Astro UI">
    Open the Deployment's settings and save without changing anything.
  </Tab>

  <Tab title="APC API">
    ```graphql theme={null}
    mutation {
      upsertDeployment(deploymentUuid: "<deployment-id>") {
        id
      }
    }
    ```

    Every other argument is optional, and omitted settings keep their current values.
  </Tab>
</Tabs>

If the Deployment is cordoned, uncordon it first. A cordoned Deployment ignores updates, so the resync won't happen. If APC's fields had actually drifted, restoring them changes the custom resource and the affected pods restart; if nothing had drifted, the update makes no change and nothing restarts.

### Never let your pipeline prune APC's Secrets

<Warning>
  **A Deployment update cannot restore deleted Secrets.** APC creates `<cr-name>-registry`, `<cr-name>-elasticsearch`, and `<cr-name>-env` once and cannot recreate them later, because it no longer holds the credentials they contain. If your pipeline prunes resources it doesn't manage, or you recreate the Deployment's namespace, and those Secrets are removed, the Deployment breaks in ways a Deployment update makes worse rather than better: the custom resource still references the missing Secrets, so pods fail to pull images or fail to start at all.

  Exclude the Deployment's namespace from pruning, or restrict pruning to the resources your pipeline created. If these Secrets are already gone, contact [Astronomer support](/docs/astro-private-cloud/v-2-x/support) to have them reissued.
</Warning>

### Check which fields APC owns

To see exactly what APC claims on a Deployment, read the custom resource's field ownership and look for the `houston` manager:

```bash theme={null}
kubectl get airflow <cr-name> -n <cr-namespace> --show-managed-fields -o yaml
```

`--show-managed-fields` is required. Without it, `kubectl` hides the ownership information from `-o yaml` and `-o json` output.

## Pause management with cordon

Cordoning a Deployment stops APC applying changes to it while it keeps running. It is not specific to adopted Deployments, so it has its own page: see [Cordon a Deployment](/docs/astro-private-cloud/v-2-x/cordon-deployment).

<Tip>
  Cordon adopted Deployments before a platform upgrade. See [Known limitations](#known-limitations).
</Tip>

## Release a Deployment

Releasing, also called unadopting, returns a Deployment to operator-only management.

<Tabs>
  <Tab title="Astro UI">
    In the Deployments list, open the Deployment's actions menu, select **Unadopt Deployment**, and confirm. Adopted Deployments carry an **Adopted** badge in the **Adopted** column.

    <Frame>
      <img src="https://mintcdn.com/astronomer/V_9YWWO4aJDSUYR5/images/astro-private-cloud/unadopt_deployment.png?fit=max&auto=format&n=V_9YWWO4aJDSUYR5&q=85&s=965de9bb02959eda0458c9b868ab31bf" alt="The Deployments list with an adopted Deployment's actions menu open on Unadopt Deployment." width="3452" height="1022" data-path="images/astro-private-cloud/unadopt_deployment.png" />
    </Frame>
  </Tab>

  <Tab title="APC API">
    ```graphql theme={null}
    mutation {
      unadoptDeployment(deploymentUuid: "<deployment-id>") {
        id
        releaseName
      }
    }
    ```
  </Tab>

  <Tab title="Astro CLI">
    ```bash theme={null}
    astro deployment unadopt --deployment-id=<deployment-id>
    ```

    The CLI asks for confirmation before releasing.
  </Tab>
</Tabs>

### What happens when you release

APC deletes its own record of the Deployment, along with its deploy history and the Deployment-scoped roles it granted. Workspace membership is left in place.

Nothing is removed from your cluster. The Airflow custom resource, its namespace, its metadata database, and its data are all left as they are, the operator continues to reconcile it, and Airflow keeps running. You can adopt the same Deployment again later.

<Warning>
  **Releasing does not undo the configuration APC applied.** In particular, the Airflow web authentication and ingress that APC applied at adoption stay on the custom resource, but APC no longer recognizes the Deployment, so users can't sign in to the Airflow UI until you restore your own web authentication configuration. Restore it as part of releasing, not afterwards.
</Warning>

Releasing is not the same as deleting. Deleting a Deployment removes the underlying Airflow and its database; releasing removes only APC's record of it.

## Known limitations

* **The cluster must already be an APC data plane.** You cannot adopt Deployments from a cluster the control plane doesn't know about. Register it first. See [Register a data plane cluster](/docs/astro-private-cloud/v-2-x/register-data-plane).
* **Airflow web authentication takeover is one-way.** Once APC applies its authentication to an adopted Deployment, there is no supported path back to your original configuration while the Deployment remains adopted.
* **Releasing a Deployment interrupts Airflow web access** until you restore its original web authentication configuration. See [Release a Deployment](#release-a-deployment).
* **A platform upgrade can restart adopted Deployments.** Cordon any adopted Deployment you don't want APC to act on during an upgrade, and uncordon it afterwards.
* **Worker settings in APC don't reach an adopted Deployment.** Worker count, worker resources, and autoscaling are accepted and stored but never applied, and changing the executor discards the Deployment's worker queues and KEDA configuration. See [Worker queues and autoscaling](#worker-queues-and-autoscaling).
* **Data plane failover does not cover adopted Deployments,** and nothing blocks you from initiating failover on a data plane that has them. Plan their recovery separately. See [Data plane failover](#data-plane-failover).
* **Images pinned by digest are converted to tag references at adoption.** The digest pin is not preserved, and the substituted tag may not exist. Re-tag before adopting. See the warning under [What adoption changes](#what-adoption-changes).
* **Plain `astro deploy` breaks a Deployment that kept its own image.** It pushes to APC's built-in registry and repoints the Deployment there, but no pull credential was provisioned for it. Use `--remote --image-name`. See [Deploy code when you keep your own image](#deploy-code-when-you-keep-your-own-image).
* **The registry and logging choices are fixed at adoption.** Changing either means releasing the Deployment and adopting it again.
* **Users can't be imported from an adopted Airflow 3 Deployment.** APC takes over its auth manager at adoption, so there are no Airflow-local users to read. [Invite them to APC](/docs/astro-private-cloud/v-2-x/manage-permissions#invite-users) instead.
* **Existing task logs are not migrated** when you route logging to APC. Only logs written after the switch are readable from the Airflow UI; older logs stay in your own store and the Airflow UI no longer resolves them.
* **Deleted APC Secrets can't be restored by a Deployment update.** If `<cr-name>-registry`, `<cr-name>-elasticsearch`, or `<cr-name>-env` is deleted or pruned, reissuing it requires Astronomer support. See [Never let your pipeline prune APC's Secrets](#never-let-your-pipeline-prune-apcs-secrets).
* **Clusters using the authentication sidecar don't get an ingress for adopted Deployments**, so the Airflow UI link is not reachable from APC on those clusters.

## Reference

### Adoption fields on a Deployment

| Field                          | Meaning                                                                                      |
| ------------------------------ | -------------------------------------------------------------------------------------------- |
| `isAdopted`                    | Whether the Deployment came from an operator-managed custom resource.                        |
| `adoptedAt`                    | When it was adopted.                                                                         |
| `adoptionLoggingManagedByApc`  | Whether APC is the task-log destination. Always `true` for Deployments APC created itself.   |
| `adoptionRegistryManagedByApc` | Whether APC's registry supplies the image. Always `true` for Deployments APC created itself. |

### API operations

| Operation                                                                         | Purpose                                                                                    |
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `adoptionCandidates(clusterId)`                                                   | List operator-managed custom resources on a cluster that aren't adopted yet. System Admin. |
| `adoptDeployment(...)`                                                            | Adopt one custom resource into a Workspace. Workspace Admin.                               |
| `adoptedAirflowUsers(deploymentId)`                                               | List the Deployment's Airflow users with suggested roles. Read-only.                       |
| `deploymentUserBulkImport(deploymentId, users)`                                   | Import the reviewed users and grant their roles.                                           |
| `cordonDeployment(deploymentUuid, reason)` / `uncordonDeployment(deploymentUuid)` | Pause and resume APC management.                                                           |
| `unadoptDeployment(deploymentUuid)`                                               | Release the Deployment back to operator-only management.                                   |

See [Use the APC API](/docs/astro-private-cloud/v-2-x/houston-api) and [Example APC API queries](/docs/astro-private-cloud/v-2-x/houston-api-example-queries) for authenticating and running these operations.

## Related documentation

* [Install a data plane cluster](/docs/astro-private-cloud/v-2-x/install-data-plane)
* [Register a data plane cluster](/docs/astro-private-cloud/v-2-x/register-data-plane)
* [Airflow Operator mode](/docs/astro-private-cloud/v-2-x/airflow-operator-mode)
* [Move the operator under APC](/docs/astro-private-cloud/v-2-x/transition-operator-to-apc)
* [Data plane architecture](/docs/astro-private-cloud/v-2-x/data-plane-architecture)
* [Data plane failover](/docs/astro-private-cloud/v-2-x/data-plane-failover)
* [Configure a Deployment](/docs/astro-private-cloud/v-2-x/configure-deployment)
* [Cordon and uncordon a Deployment](/docs/astro-private-cloud/v-2-x/cordon-deployment)
* [Environment variables](/docs/astro-private-cloud/v-2-x/environment-variables)
* [Deploy code overview](/docs/astro-private-cloud/v-2-x/deploy-code-overview)
* [Configure logging](/docs/astro-private-cloud/v-2-x/logs-configuration)
* [Deployment metrics](/docs/astro-private-cloud/v-2-x/deployment-metrics)
* [Manage permissions](/docs/astro-private-cloud/v-2-x/manage-permissions)
* [Manage platform users](/docs/astro-private-cloud/v-2-x/manage-platform-users)
* [Invite users to a Workspace or Deployment](/docs/astro-private-cloud/v-2-x/manage-permissions#invite-users)
