|
| 1 | +# Leantime Deployment via ArgoCD |
| 2 | + |
| 3 | +This deployment follows the GitOps-first pattern using ArgoCD to deploy Leantime directly from its upstream Git repository. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +- **Application**: Leantime - Project management for lean teams |
| 8 | +- **Deployment Method**: ArgoCD Helm-from-Git |
| 9 | +- **Upstream Repo**: https://github.com/Leantime/leantime.git |
| 10 | +- **Chart Version**: v3.6.0 (pinned) |
| 11 | +- **Namespace**: apps-leantime |
| 12 | +- **Access**: Tailscale-only via Ingress |
| 13 | +- **Secrets**: 1Password Operator |
| 14 | + |
| 15 | +## Architecture |
| 16 | + |
| 17 | +``` |
| 18 | +┌─────────────────────────────────────────────────────────────┐ |
| 19 | +│ ArgoCD Root App (argocd/root.yml) │ |
| 20 | +│ Discovers all apps in argocd/apps/** recursively │ |
| 21 | +└─────────────────────────────────────────────────────────────┘ |
| 22 | + │ |
| 23 | + ┌─────────────────────┼─────────────────────┐ |
| 24 | + │ │ │ |
| 25 | + ▼ ▼ ▼ |
| 26 | +┌───────────────┐ ┌───────────────┐ ┌───────────────┐ |
| 27 | +│ leantime- │ │ leantime │ │ leantime- │ |
| 28 | +│ secrets │ │ │ │ ingress │ |
| 29 | +│ (wave 10) │ │ (wave 20) │ │ (wave 30) │ |
| 30 | +└───────────────┘ └───────────────┘ └───────────────┘ |
| 31 | + │ │ │ |
| 32 | + │ │ │ |
| 33 | + ▼ ▼ ▼ |
| 34 | +┌───────────────┐ ┌───────────────┐ ┌───────────────┐ |
| 35 | +│ 1Password │ │ Helm Chart │ │ Tailscale │ |
| 36 | +│ Operator │ │ from Git │ │ Ingress │ |
| 37 | +│ creates │ │ leantime/ │ │ + Homepage │ |
| 38 | +│ Secrets │ │ leantime.git │ │ annotations │ |
| 39 | +└───────────────┘ └───────────────┘ └───────────────┘ |
| 40 | +``` |
| 41 | + |
| 42 | +## Files Created |
| 43 | + |
| 44 | +### ArgoCD Applications |
| 45 | +- `argocd/apps/apps/leantime-secrets.yml` - Deploys 1Password CRDs (sync-wave 10) |
| 46 | +- `argocd/apps/apps/leantime.yml` - Deploys Leantime Helm chart (sync-wave 20) |
| 47 | +- `argocd/apps/apps/leantime-ingress.yml` - Deploys Tailscale ingress (sync-wave 30) |
| 48 | + |
| 49 | +### Kubernetes Manifests |
| 50 | +- `k8s/leantime_secrets/` - OnePasswordItem CRDs and kustomization |
| 51 | + - `onepassword-db.yml` - Database credentials |
| 52 | + - `onepassword-app.yml` - App secrets (reserved for future use) |
| 53 | + - `kustomization.yml` - Kustomize manifest |
| 54 | + - `README.md` - Setup instructions |
| 55 | + |
| 56 | +- `k8s/leantime_ingress/` - Tailscale ingress with Homepage annotations |
| 57 | + - `ingress.yml` - Ingress resource |
| 58 | + |
| 59 | +## Prerequisites |
| 60 | + |
| 61 | +Before deploying, ensure the following are in place: |
| 62 | + |
| 63 | +1. **ArgoCD** is installed and the root app is configured |
| 64 | +2. **Tailscale Operator** is deployed and configured |
| 65 | +3. **1Password Operator** is deployed and configured |
| 66 | +4. **1Password Items** are created in the HomeLab vault: |
| 67 | + - Item: `Leantime Database` with fields: |
| 68 | + - `mariadb-root-password` |
| 69 | + - `mariadb-password` |
| 70 | + - Item: `Leantime App Secrets` (can be empty for now) |
| 71 | + |
| 72 | +5. **Storage Class** `nfs-synology-retain` is available in the cluster |
| 73 | + |
| 74 | +## Deployment Details |
| 75 | + |
| 76 | +### Secrets (Sync-Wave 10) |
| 77 | + |
| 78 | +The `leantime-secrets` application deploys OnePasswordItem CRDs that reference items in 1Password. The 1Password Operator materializes these as Kubernetes Secrets: |
| 79 | + |
| 80 | +- `leantime-db`: Contains MariaDB credentials |
| 81 | +- `leantime-app`: Reserved for future app-level secrets |
| 82 | + |
| 83 | +### Application (Sync-Wave 20) |
| 84 | + |
| 85 | +The `leantime` application uses ArgoCD's Helm-from-Git feature to render the chart directly from the upstream repository: |
| 86 | + |
| 87 | +**Source Configuration**: |
| 88 | +- Repository: `https://github.com/Leantime/leantime.git` |
| 89 | +- Revision: `v3.6.0` (immutable tag) |
| 90 | +- Path: `helm` |
| 91 | + |
| 92 | +**Key Helm Values**: |
| 93 | +- Image tag pinned to `3.6.0` |
| 94 | +- Persistence enabled with `nfs-synology-retain` storage class (10Gi) |
| 95 | +- Built-in ingress disabled |
| 96 | +- MariaDB subchart configured to use `existingSecret: leantime-db` |
| 97 | +- Session password set to a generated secure value |
| 98 | + |
| 99 | +**Chart Limitations**: |
| 100 | +The upstream Leantime chart does not support `existingSecret` for application-level secrets (session password, SMTP). The session password is therefore included in the Helm values as a generated secure random value. Database credentials properly use the MariaDB subchart's `existingSecret` feature. |
| 101 | + |
| 102 | +### Ingress (Sync-Wave 30) |
| 103 | + |
| 104 | +The `leantime-ingress` application deploys a Tailscale Ingress that: |
| 105 | + |
| 106 | +- Uses `ingressClassName: tailscale` |
| 107 | +- Exposes Leantime at `leantime.rohu-shark.ts.net` (MagicDNS) |
| 108 | +- Routes to the Leantime service on port 80 |
| 109 | +- Includes Homepage annotations for service discovery: |
| 110 | + - Name: "Leantime" |
| 111 | + - Group: "Apps" |
| 112 | + - Icon: "leantime.png" |
| 113 | + |
| 114 | +## Access |
| 115 | + |
| 116 | +Once deployed, Leantime will be accessible only from devices on your Tailnet at: |
| 117 | + |
| 118 | +**URL**: https://leantime.rohu-shark.ts.net/ |
| 119 | + |
| 120 | +The service will also appear on your Homepage dashboard in the "Apps" group. |
| 121 | + |
| 122 | +## Security |
| 123 | + |
| 124 | +- ✅ No secrets in Git |
| 125 | +- ✅ Database credentials managed by 1Password |
| 126 | +- ✅ No public ingress (Tailscale-only) |
| 127 | +- ✅ Immutable upstream version pinned (v3.6.0) |
| 128 | +- ⚠️ Session password in Helm values (chart limitation) |
| 129 | + |
| 130 | +## Verification |
| 131 | + |
| 132 | +After ArgoCD syncs all three applications: |
| 133 | + |
| 134 | +```bash |
| 135 | +# Check ArgoCD application status |
| 136 | +kubectl get applications -n argocd | grep leantime |
| 137 | + |
| 138 | +# Check secrets created by 1Password Operator |
| 139 | +kubectl get secrets -n apps-leantime |
| 140 | + |
| 141 | +# Check the Leantime deployment |
| 142 | +kubectl get deployments -n apps-leantime |
| 143 | + |
| 144 | +# Check the MariaDB statefulset |
| 145 | +kubectl get statefulsets -n apps-leantime |
| 146 | + |
| 147 | +# Check the Tailscale ingress |
| 148 | +kubectl get ingress -n apps-leantime |
| 149 | + |
| 150 | +# Check Tailscale proxy device |
| 151 | +kubectl get pods -n apps-leantime | grep ts- |
| 152 | +``` |
| 153 | + |
| 154 | +All applications should show as `Synced` and `Healthy` in ArgoCD. |
| 155 | + |
| 156 | +## Troubleshooting |
| 157 | + |
| 158 | +### Application won't sync |
| 159 | +- Verify 1Password items exist in the HomeLab vault |
| 160 | +- Check ArgoCD application events: `kubectl describe application leantime -n argocd` |
| 161 | + |
| 162 | +### Database connection issues |
| 163 | +- Verify secrets were created: `kubectl get secrets -n apps-leantime` |
| 164 | +- Check secret keys: `kubectl get secret leantime-db -n apps-leantime -o yaml` |
| 165 | +- Ensure keys `mariadb-root-password` and `mariadb-password` are present |
| 166 | + |
| 167 | +### Ingress not accessible |
| 168 | +- Verify Tailscale Operator is running |
| 169 | +- Check ingress status: `kubectl describe ingress leantime -n apps-leantime` |
| 170 | +- Look for Tailscale proxy pod: `kubectl get pods -n apps-leantime` |
| 171 | +- Check MagicDNS resolution from a Tailnet device |
| 172 | + |
| 173 | +## Future Improvements |
| 174 | + |
| 175 | +1. If the upstream chart adds `existingSecret` support for app secrets, update to use `leantime-app` secret |
| 176 | +2. Consider enabling SMTP for email notifications (would need additional 1Password fields) |
| 177 | +3. Evaluate S3 storage for user files instead of NFS PVCs |
0 commit comments