# Deploy apps with GitOps using Argo CD

Source: https://docs.quake.ai/resources/deployments/deploy-argocd-gitops
Markdown: https://docs.quake.ai/resources/deployments/deploy-argocd-gitops.md

---

# Deploy apps with GitOps using Argo CD

Argo CD is a GitOps controller for Kubernetes. It watches a git repository that holds your Kubernetes manifests and reconciles the cluster to match what the repository declares. You change the repository, and Argo CD applies the change. Someone edits a live resource by hand, and Argo CD reverts it to the committed state.

Stand up Argo CD on a Quake AI Kubernetes cluster from the [validated OpenTofu template](/docs/platform/validation#how-infrastructure-templates-are-checked) `k8s-cluster`. You install Argo CD, connect a git repository that holds a sample app, deploy it with a sync, watch a repository change roll out, and watch Argo CD correct manual drift.



Argo CD is open-source software you run inside your own cluster. You install it, you upgrade it, you manage its access control and its admin credentials, and you own the cluster underneath. Quake AI provides the compute, the network, and the Kubernetes cluster. There is no managed Argo CD control plane.



<Figure size="md" caption="Argo CD runs in the cluster, watches a git repository of Kubernetes manifests, and reconciles the running app to match each commit">

```d2
direction: right

dev: Developer {shape: person}
git: Git repository\nKubernetes manifests

cluster: Kubernetes cluster {
  argocd: Argo CD\ncontroller
  app: Sample app\nDeployment + Service
  argocd -> app: applies + reconciles
}

dev -> git: commit + push
git -> cluster.argocd: watched source
dev -> cluster.argocd: UI + CLI over tunnel
```

</Figure>

<PricingCompanion
  components={[
    { kind: "template", slug: "k8s-cluster", required: true },
  ]}
/>

## Prerequisites

You need:

- A running Kubernetes cluster on Quake AI from the [Kubernetes cluster bootstrap template](/resources/deployments/deploy-k8s-cluster-template). The managed Kubernetes path in [Deploy your first app on Kubernetes](/resources/deployments/deploy-first-app) works the same way once you have `kubectl` access.
- `kubectl` configured to reach that cluster. Run `kubectl get nodes` and confirm every node reports `Ready`.
- The [Argo CD CLI](https://argo-cd.readthedocs.io/en/stable/cli_installation/) installed on your workstation.
- A git repository you can push to (GitHub, GitLab, or a self-hosted forge). The repository can be public for a first sync; Argo CD also connects to private repositories with credentials.



The `k8s-cluster` template exposes the Kubernetes API on the private subnet only, so you run `kubectl` on the control plane over SSH. Set up the `k8s` shell helper from [step 8 of the Kubernetes cluster bootstrap deployment](/resources/deployments/deploy-k8s-cluster-template) and substitute `k8s` for `kubectl` in the commands below. For the Argo CD UI in step 3, forward the port over the same SSH connection.



## Step 1: Confirm cluster access

Check that `kubectl` reaches the cluster and every node is ready before you install anything:

```bash
kubectl get nodes
```

Every node should report `Ready`. If the command times out or returns an authentication error, fix cluster access before continuing.

## Step 2: Install Argo CD

Create the `argocd` namespace and apply the upstream install manifest:

```bash
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
```

Wait for the API server deployment to roll out:

```bash
kubectl -n argocd rollout status deploy/argocd-server --timeout=300s
```

The command returns `deployment "argocd-server" successfully rolled out` once Argo CD is running. Confirm the pods:

```bash
kubectl -n argocd get pods
```

Each pod should report `Running`.

## Step 3: Reach the Argo CD UI and read the admin password

Argo CD installs without a public endpoint. Forward its API port to your workstation:

```bash
kubectl -n argocd port-forward svc/argocd-server 8080:443
```

Leave that command running and open a second terminal. Read the initial admin password from the secret Argo CD generated at install:

```bash
kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d; echo
```

With the port-forward open, visit `https://localhost:8080` in your browser. The certificate is self-signed, so accept the browser warning. Log in with the username `admin` and the password you read above.



The initial password is stored in plain text in the `argocd-initial-admin-secret`. Change it after your first login (under **User Info** > **Update Password**) and delete the secret. Before you share the instance with others, configure single sign-on instead of the local admin account. See the [Argo CD user management docs](https://argo-cd.readthedocs.io/en/stable/operator-manual/user-management/).



## Step 4: Log in with the CLI

With the port-forward from step 3 still running, log in from the Argo CD CLI:

```bash
argocd login localhost:8080 --username admin --insecure
```

Enter the admin password when prompted. The `--insecure` flag accepts the self-signed certificate served over the port-forward. Confirm the connection:

```bash
argocd version
```

## Step 5: Prepare a git repository of manifests

Argo CD deploys what your git repository declares. You can author manifests by
hand, or render them from a [`quake.yaml`](/resources/ai-assisted-development/quake-yaml)
manifest using the reference GitOps renderer in `consumers/gitops/`.

### Option A: Render from `quake.yaml`

If your app already carries a `quake.yaml` launch manifest, generate Kubernetes
manifests and commit them instead of writing each resource by hand:

```bash
# Produce a handoff packet from your manifest (MCP prepare_launch, or export from CI)
python3 consumers/gitops/quake_yaml_k8s_renderer.py \
  --packet handoff.json \
  --out-dir manifests \
  --environments production \
  --repo-url https://github.com/YOUR_USER/YOUR_REPO \
  --image ghcr.io/YOUR_USER/web:main
```

The renderer writes a `production/` directory (Deployment, Service, Ingress when
`domain` is set, optional in-cluster Postgres/Redis, and Secret stubs for names
only). It also writes `argocd/application-production.yaml` you can apply or
adapt. Populate `app-secrets` and datastore auth Secrets out of band before the
first sync; the renderer never writes secret values into git. See `consumers/gitops/README.md`
in the platform repository for the full mapping table and unmapped-service behavior.

Commit the output and push:

```bash
git add manifests/
git commit -m "Add rendered web app manifests"
git push
```

Skip to step 6 and set `path: production` (or `path: manifests/production` if
you nested the output).

### Option B: Hand-written sample app

Create a `manifests` directory in your repository with a small app.

`manifests/deployment.yaml`:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27-alpine
          ports:
            - containerPort: 80
```

`manifests/service.yaml`:

```yaml
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
```

Commit both files and push:

```bash
git add manifests/
git commit -m "Add web app manifests"
git push
```

## Step 6: Create an Argo CD Application

An `Application` resource tells Argo CD which repository to watch, which path in it to apply, and where to deploy. Create `application.yaml` on your workstation and set `repoURL` to your repository:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/YOUR_USER/YOUR_REPO
    targetRevision: main
    path: manifests
  destination:
    server: https://kubernetes.default.svc
    namespace: web
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
```

Apply the Application to the cluster:

```bash
kubectl apply -f application.yaml
```

`automated` turns on continuous reconciliation: `prune` removes resources you delete from the repository, and `selfHeal` reverts manual changes to live resources. `CreateNamespace=true` creates the `web` namespace on the first sync.

## Step 7: Sync and verify the deployment

With `automated` set, Argo CD syncs on its own within a few minutes. Trigger it immediately instead of waiting:

```bash
argocd app sync web
```

Check that the Application reports healthy and synced:

```bash
argocd app get web
```

Look for `Sync Status: Synced` and `Health Status: Healthy`. Confirm the workload directly:

```bash
kubectl -n web get deployment web
kubectl -n web get pods
```

You should see two `web` pods in `Running` state.

## Step 8: Roll out a change from git

Change the running app by changing the repository. Edit `manifests/deployment.yaml` and set `replicas: 3`, then commit and push:

```bash
git commit -am "Scale web to 3 replicas"
git push
```

Argo CD detects the new commit on its next poll (every 3 minutes by default) and reconciles. To apply it at once, sync again:

```bash
argocd app sync web
kubectl -n web get pods
```

A third `web` pod starts. The repository is the source of truth: the cluster now runs what `main` declares.

## Step 9: Watch Argo CD correct drift

Because `selfHeal` is on, Argo CD reverts changes made directly against the cluster. Scale the deployment by hand to create drift:

```bash
kubectl -n web scale deployment/web --replicas=10
kubectl -n web get pods --watch
```

Argo CD compares the live state to the repository, finds 10 replicas where the manifest declares 3, and scales the deployment back to 3 within seconds. Press `Ctrl+C` to stop watching once the pod count settles. The committed state wins over the manual edit.



Argo CD is one of two widely used GitOps controllers. [Flux](https://fluxcd.io/) follows the same pull-based model with a different architecture and a CLI-driven workflow instead of a built-in UI. These steps install Argo CD. The cluster setup and the git-as-source-of-truth pattern carry over if you choose Flux.



## What you built

- **Installed Argo CD** into its own namespace from the upstream manifest
- **Reached the Argo CD UI and CLI** over a port-forward without exposing a public endpoint
- **Connected a git repository** of Kubernetes manifests as the deployment source
- **Created an Application** with automated sync, pruning, and self-heal
- **Rolled out a change** by committing to the repository
- **Watched Argo CD correct drift** by reverting a manual edit to the committed state

## Operating this deployment

You own Argo CD and the cluster it runs on. Plan for the operational work:

- **Upgrades.** Track Argo CD releases and reapply the pinned manifest version on your own schedule. Read the [upgrade notes](https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/overview/) before a major version jump.
- **Access control.** Replace the local `admin` account with single sign-on and define RBAC roles before more than one person uses the instance.
- **Secrets.** Keep secret values out of the git repository. Use a secrets controller (for example the [External Secrets Operator](https://external-secrets.io/) or sealed secrets) so Argo CD applies references, not plaintext credentials. See [How to store application secrets and inject them at runtime](/docs/security/how-to/inject-app-secrets).
- **Backups.** The Application definitions live in git, but the Argo CD configuration (projects, repositories, RBAC) lives in the cluster. Back up the `argocd` namespace or manage its configuration as declarative files in git as well.

## Next steps

- [Deploy the Kubernetes cluster bootstrap template with OpenTofu](/resources/deployments/deploy-k8s-cluster-template): provision the cluster this deployment runs on
- [How to deploy to a Quake AI Kubernetes cluster from CI](/docs/kubernetes/how-to/deploy-from-ci): the push-based pipeline alternative to GitOps
- [Deploy your first app on Kubernetes](/resources/deployments/deploy-first-app): the managed Kubernetes path
- [Kubernetes platforms](/resources/solutions/kubernetes-platforms): the broader pattern for building on a self-managed cluster

## Clean up

Remove the Application and Argo CD, then release the cluster. Deleting the Application with the cascade also removes the `web` namespace it created:

```bash
argocd app delete web --cascade
kubectl delete namespace argocd
```

If you provisioned the cluster only for this deployment, destroy it from the template directory:

```bash
tofu destroy
```

Follow the clean-up steps in [Deploy the Kubernetes cluster bootstrap template with OpenTofu](/resources/deployments/deploy-k8s-cluster-template) for the full teardown, and remove any kubeconfig or SSH key files you no longer need from your workstation.
