> ## 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.

# Deploy Dags with NFS

You can use an external [Network File System (NFS) Volume](https://kubernetes.io/docs/concepts/storage/volumes/#nfs) to deploy Dags to an Airflow Deployment on Astro Private Cloud (APC).

Unlike deploying Dags with the Astro CLI, deploying Dags to an NFS volume doesn't require rebuilding a Docker image and restarting your underlying Airflow service. When a Dag is added to an NFS volume, it automatically appears in the Airflow UI without requiring additional action or causing downtime.

## How NFS deploys work

When you configure an NFS volume for a Deployment:

1. APC validates the NFS location format (`SERVER_IP:PATH`).
2. APC creates a Kubernetes PersistentVolume (PV) pointing to your NFS server.
3. APC creates a PersistentVolumeClaim (PVC) bound to the PV.
4. The NFS volume is mounted read-only to the scheduler and workers at `/usr/local/airflow/dags`.
5. Dags are synced by writing files directly to your NFS server.

```text wrap theme={null}
NFS Server (/dags)
       │
       ▼
┌──────────────┐
│ Kubernetes   │
│ PV + PVC     │
└──────┬───────┘
       │
       ├──► Scheduler (/usr/local/airflow/dags)
       │
       └──► Workers (/usr/local/airflow/dags)
```

## Implementation considerations

<Warning>
  If you configure NFS for a Deployment, you can't use the Astro CLI or service accounts to deploy Dags to that Deployment. NFS becomes the exclusive deployment mechanism.
</Warning>

Before configuring NFS deploys:

* **Namespace pools limitation**: NFS deploys won't work if you use [namespace pools](/docs/astro-private-cloud/v-2-x/namespace-pools) and set `global.clusterRoles` to `false`. The NFS deploy feature requires creating PersistentVolumes, which are cluster-scoped resources.
* **Dags only**: NFS volumes deploy only Dags. To add Python dependencies or system packages, update your `requirements.txt` and `packages.txt` files and deploy using the CLI or CI/CD.
* **Airflow version**: NFS volumes require Airflow 2.0 or later.
* **Read-only mount**: The NFS volume is mounted read-only to Airflow components. Write operations must happen directly on the NFS server.

## Prerequisites

* APC 2.0 or later installed
* An NFS server accessible from your Kubernetes cluster
* Network connectivity between cluster nodes and the NFS server
* Read access configured for UID/GID `50000` on the NFS share

## Enable NFS volume storage

A System Admin must enable NFS deploys on the platform. Update your `values.yaml`:

```yaml wrap theme={null}
houston:
  config:
    deployments:
      configureDagDeployment: true
      nfsMountDagDeployment: true
```

Apply the configuration change:

```bash wrap theme={null}
helm upgrade astronomer astronomer/astronomer \
  -f values.yaml \
  --namespace astronomer
```

## Provision an NFS volume

<Tabs>
  <Tab title="AWS (EFS)">
    1. Create an [EFS file system](https://docs.aws.amazon.com/efs/latest/ug/getting-started.html).
    2. Configure security groups to allow NFS traffic (port 2049) from your EKS nodes.
    3. Create an access point or use the root directory.
    4. Note the file system DNS name: `fs-xxxxxxxx.efs.region.amazonaws.com`.
  </Tab>

  <Tab title="GCP (Filestore)">
    1. Create a [Filestore instance](https://cloud.google.com/filestore/docs/creating-instances).
    2. Configure firewall rules to allow traffic from your GKE nodes.
    3. Create a file share directory for Dags.
    4. Configure [IP-based access control](https://cloud.google.com/filestore/docs/creating-instances#configuring_ip-based_access_control) for UID/GID 50000.
    5. Note the instance IP address.
  </Tab>

  <Tab title="Azure (Azure Files)">
    1. Create a [Premium File Storage account](https://docs.microsoft.com/en-us/azure/storage/files/storage-files-how-to-create-nfs-shares).
    2. Create an NFS file share.
    3. Configure network access from your AKS cluster.
    4. Note the mount address: `<storage-account>.file.core.windows.net:/<storage-account>/<share-name>`.
  </Tab>

  <Tab title="Self-managed">
    For on-premises or self-managed NFS servers:

    1. Ensure the NFS server exports a directory with appropriate permissions.
    2. Configure `/etc/exports` to allow access from Kubernetes node IPs.
    3. Set ownership to UID/GID 50000 or configure `no_root_squash` appropriately.

    Example `/etc/exports`:

    ```text wrap theme={null}
    /srv/airflow-dags 10.0.0.0/8(rw,sync,no_subtree_check,all_squash,anonuid=50000,anongid=50000)
    ```
  </Tab>
</Tabs>

## Configure NFS for a Deployment

<Tabs>
  <Tab title="UI">
    1. In the APC UI, create a new Deployment or open an existing one.
    2. Go to **DAG Deployment** in the Deployment settings.
    3. Select **NFS Volume Mount** as the mechanism.
    4. Enter the NFS location in `IP:PATH` format:
       * AWS EFS: `10.0.0.1:/`
       * GCP Filestore: `10.0.0.1:/dags`
       * Azure Files: `storage-account.file.core.windows.net:/storage-account/share-name`
    5. Click **Save** or **Deploy Changes**.
  </Tab>

  <Tab title="CLI">
    Create a new Deployment with NFS:

    ```bash wrap theme={null}
    astro deployment create my-deployment \
      --nfs-location "192.168.0.1:/dags"
    ```

    Update an existing Deployment to use NFS:

    ```bash wrap theme={null}
    astro deployment update <deployment-id> \
      --nfs-location "192.168.0.1:/dags"
    ```
  </Tab>

  <Tab title="API">
    Use `upsertDeployment` to configure a Deployment's Dag deployment mechanism as an NFS volume mount:

    ```graphql wrap theme={null}
    mutation {
      upsertDeployment(
        workspaceUuid: "<workspace-uuid>"
        label: "my-deployment"
        dagDeployment: {
          type: volume
          nfsLocation: "192.168.0.1:/dags"
        }
      ) {
        id
        releaseName
      }
    }
    ```
  </Tab>
</Tabs>

## Deploy Dags to NFS volume

Once configured, deploy Dags by copying files to your NFS server. The method depends on your infrastructure:

### Direct copy

```bash wrap theme={null}
# Copy DAGs to NFS mount point
cp -r dags/* /mnt/nfs/dags/
```

### Use kubectl

If you have a pod with NFS access:

```bash wrap theme={null}
kubectl cp dags/ deployment-namespace/nfs-sync-pod:/dags/
```

### CI/CD integration

Example GitHub Actions workflow:

```yaml wrap theme={null}
name: Deploy DAGs to NFS
on:
  push:
    branches: [main]
    paths: ['dags/**']

jobs:
  deploy:
    runs-on: self-hosted  # Runner with NFS access
    steps:
      - uses: actions/checkout@v4
      - name: Sync DAGs
        run: |
          rsync -av --delete dags/ /mnt/nfs/airflow-dags/
```

### Sync from cloud storage

For cloud-native workflows, sync from object storage:

```bash wrap theme={null}
# AWS S3 to EFS
aws s3 sync s3://my-bucket/dags/ /mnt/efs/dags/

# GCS to Filestore  
gsutil -m rsync -r gs://my-bucket/dags/ /mnt/filestore/dags/

# Azure Blob to Azure Files
azcopy sync "https://account.blob.core.windows.net/dags" "/mnt/azure/dags"
```

## Verify NFS configuration

Check that the PV and PVC were created:

```bash wrap theme={null}
# List PersistentVolumes
kubectl get pv | grep dags

# List PersistentVolumeClaims in the deployment namespace
kubectl get pvc -n <deployment-namespace> | grep dags
```

Verify the volume is mounted in Airflow Pods:

```bash wrap theme={null}
kubectl exec -n <deployment-namespace> <scheduler-pod> -- \
  ls -la /usr/local/airflow/dags
```

## Troubleshooting

### Dags aren't appearing

1. **Check NFS connectivity**:

   ```bash wrap theme={null}
   kubectl exec -n <namespace> <pod> -- \
     showmount -e <nfs-server-ip>
   ```

2. **Verify mount permissions**:

   ```bash wrap theme={null}
   kubectl exec -n <namespace> <scheduler-pod> -- \
     ls -la /usr/local/airflow/dags
   ```

3. **Check PV/PVC status**:

   ```bash wrap theme={null}
   kubectl describe pv <deployment>-dags-<hash>
   kubectl describe pvc -n <namespace> <deployment>-dags-<hash>
   ```

### Permission denied errors

Ensure your NFS export allows access for UID/GID 50000:

```bash wrap theme={null}
# On NFS server
chown -R 50000:50000 /srv/airflow-dags
chmod -R 755 /srv/airflow-dags
```

### Stale file handle

If pods report stale NFS handles after server restart:

```bash wrap theme={null}
# Restart affected pods
kubectl rollout restart deployment -n <namespace> <scheduler>
kubectl rollout restart statefulset -n <namespace> <workers>
```

### Network connectivity issues

Verify NFS port (2049) is accessible:

```bash wrap theme={null}
kubectl run nfs-test --rm -it --image=busybox -- \
  nc -zv <nfs-server-ip> 2049
```

## Security considerations

* **Network isolation**: Use network policies to restrict which pods can access the NFS server.
* **Access control**: Configure NFS exports to allow only Kubernetes node IPs.
* **Read-only mounts**: APC mounts NFS volumes read-only to prevent accidental modifications from Airflow.
* **Audit logging**: Enable NFS server audit logging for compliance requirements.

## Alternative: Git-sync deploys

If NFS infrastructure isn't available, consider [git-sync deploys](/docs/astro-private-cloud/v-2-x/deploy-git-sync) which pull Dags from a Git repository. Git-sync provides:

* Version control for Dags.
* No external storage infrastructure required.
* Webhook-based or polling synchronization.
* Branch-based deployment strategies.
