Skip to main content

Argo CD

Argo CD runs in the cluster and reconciles the GitOps repositories (homelabs and homelabs-private). Install the in-cluster manifests, then register applications through app-of-apps. Every private Git source needs repository credentials.

Install
#

kubectl apply -k argocd
kubectl -n argocd get pods

Initial admin password:

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

Reach the UI
#

Service argocd-server is ClusterIP. There is no Cloudflare Public Hostname for Argo CD, and the UI does not use Dex. argocd-cm has no url or oidc.config; do not add an argocd Dex static client. Anyone who can kubectl already has cluster credentials.

Forward the HTTPS Service and accept the self-signed certificate in the browser:

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

Then open https://localhost:8080. Log in as admin with the initial admin secret above. The local admin account stays enabled.

The argocd CLI can use the same forward (argocd login localhost:8080 --insecure) or talk through your kubeconfig without a public URL (argocd login --core on a context that can reach namespace argocd).

Dex still publishes dex.<your-domain> for kubectl OIDC. That path does not log you into the Argo CD UI. Do not put Cloudflare Access in front of Dex; OIDC discovery and JWKS must stay reachable.

Repository authentication
#

Argo CD needs credentials for every private Git repo used as a source. Without them, apps show ComparisonError: authentication required: Repository not found.

  1. Create a GitHub Personal Access Token (classic) with repo scope.
  2. For each private repo, create a repository secret. Replace placeholders:
kubectl create secret generic SECRET_NAME \
  -n argocd \
  --from-literal=type=git \
  --from-literal=url=REPO_URL \
  --from-literal=password=YOUR_GITHUB_TOKEN \
  --from-literal=username=YOUR_GITHUB_USERNAME

kubectl label secret SECRET_NAME -n argocd argocd.argoproj.io/secret-type=repository

Repos used by this setup:

Secret nameRepo URL
homelabs-private-repohttps://github.com/<owner>/homelabs-private.git
website-repohttps://github.com/<owner>/website.git

When adding an application that uses another private repo, extend the AppProject (argocd/projects/homelab.yaml) and create a matching repository secret.

Rotate an expired GitHub token
#

When the PAT expires, applications fail with authentication errors. Recreate the secret (example for homelabs-private):

NEW_GITHUB_PAT=ghp_xxx

kubectl create secret generic homelabs-private-repo \
  -n argocd \
  --from-literal=type=git \
  --from-literal=url=https://github.com/<owner>/homelabs-private.git \
  --from-literal=password="${NEW_GITHUB_PAT}" \
  --from-literal=username=YOUR_GITHUB_USERNAME \
  -o yaml --dry-run=client | kubectl apply -f -

The argocd.argoproj.io/secret-type=repository label is only needed on first create.

Single token for all repos under an owner
#

Use a repository credentials secret for every repo under https://github.com/<owner>:

export NEW_GITHUB_PAT=ghp_xxx
export GITHUB_USERNAME=your-github-username

kubectl create secret generic github-repo-creds \
  -n argocd \
  --from-literal=url=https://github.com/<owner> \
  --from-literal=username="${GITHUB_USERNAME}" \
  --from-literal=password="${NEW_GITHUB_PAT}" \
  -o yaml --dry-run=client | kubectl apply -f -

kubectl label secret github-repo-creds -n argocd argocd.argoproj.io/secret-type=repo-creds --overwrite

Website image pull secret (GHCR)
#

If the website image is private, the pull secret lives in homelabs-private/clusters/<cluster>/overlays/website/secret.yaml. Generate a base64 Docker config with a PAT that has read:packages:

echo -n '{"auths":{"ghcr.io":{"username":"USERNAME","password":"YOUR_GITHUB_PAT","auth":"'$(echo -n "USERNAME:YOUR_GITHUB_PAT" | base64 -w0)'"}}}' | base64 -w0

On macOS, omit -w0 from base64. Paste the output into secret.yaml under data.dockerconfigjson.

Disable Git submodules (if needed)
#

If a repo uses submodules (e.g. Hugo theme), disable submodule fetch in Argo CD:

kubectl patch configmap argocd-cmd-params-cm -n argocd --type merge -p '{"data":{"reposerver.enable.git.submodule":"false"}}'
kubectl rollout restart deployment argocd-repo-server -n argocd

Deploy applications
#

After repository authentication is configured, apply AppProject resources, then deploy app-of-apps (it discovers Application resources under clusters/<cluster>/argocd/applications/):

kubectl apply -k argocd
kubectl apply -f clusters/<cluster>/argocd/app-of-apps.yaml

Delete an application
#

Remove the app definition from Git: clusters/<cluster>/argocd/applications/<app-name>.yaml, the app repo and destination entries from argocd/projects/homelab.yaml, and the optional overlay at clusters/<cluster>/overlays/<app-name>/. Then follow Argo CD app deletion.

Application tracking model
#

This setup uses app-of-apps with <cluster>-apps creating child applications. Sync <cluster>-apps when changing files under clusters/<cluster>/argocd/applications/.

Related application docs#