# Infrastructure & Deployment Architecture

## Overview

LunarFront runs on DigitalOcean. Each customer is a fully isolated deployment — their own Kubernetes namespace, their own database. There is no multi-tenancy in the application layer.

**Stack:** DOKS (Kubernetes) · ArgoCD · Helm · Gitea · Terraform · Ansible

The guiding principle is simplicity — Docker Compose on Droplets for Gitea, Kubernetes for customer app instances. No surprise bills.

---

## Monthly Cost

| Resource | Spec | Cost |
|---|---|---|
| DOKS cluster | Control plane | $12/mo |
| DOKS nodes (2x s-2vcpu-4gb) | Runs all customer apps | ~$48/mo |
| Managed Postgres (shared) | All customer databases | $15-25/mo |
| Managed Redis (shared) | All customer queues/cache | $15/mo |
| Gitea Droplet | 1 vCPU, 1GB RAM | $6/mo |
| Gitea Postgres | Dedicated managed Postgres | $15/mo |
| Spaces | Registry, backups, files | ~$21/mo |
| **Fixed total** | | **~$132-142/mo** |
| **Per customer** | New database in shared Postgres | **~$0 marginal** |

---

## Environments

| Environment | Hostname | Purpose |
|---|---|---|
| Production (per customer) | `{customer}.app.lunarfront.tech` | Live customer instance on DOKS |
| Gitea | `git.lunarfront.tech` | Source control, CI/CD, container registry |
| Dev | Local / feature namespace on DOKS | Testing and staging |

---

## DNS

Managed through Cloudflare. Records defined in Terraform.

| Record | Type | Points To |
|---|---|---|
| `git.lunarfront.tech` | A | Gitea Droplet |
| `git-ssh.lunarfront.tech` | A | Gitea Droplet (SSH port 2222) |
| `registry.lunarfront.tech` | A | Gitea Droplet (container registry) |
| `{customer}.app.lunarfront.tech` | CNAME | DOKS load balancer |

---

## Infrastructure (Terraform)

Terraform manages all DigitalOcean and Cloudflare resources. State stored in Spaces.

```
/terraform
  main.tf         providers, backend (Spaces state)
  variables.tf
  cluster.tf      DOKS cluster + node pools
  databases.tf    shared Postgres, shared Redis
  gitea.tf        Gitea Droplet
  spaces.tf       files, backups, tf-state buckets
  dns.tf          Cloudflare DNS records
  outputs.tf      cluster endpoint, DB URLs
  terraform.tfvars  secrets (gitignored)
```

### State Backend

```hcl
terraform {
  backend "s3" {
    endpoint                    = "https://nyc3.digitaloceanspaces.com"
    bucket                      = "lunarfront-terraform-state"
    key                         = "terraform.tfstate"
    region                      = "us-east-1"
    skip_credentials_validation = true
    skip_metadata_api_check     = true
    skip_region_validation      = true
    force_path_style            = true
  }
}
```

---

## Gitea (Source Control + CI/CD)

Gitea runs on its own Droplet with a dedicated managed Postgres. It hosts:
- Source code repositories
- Gitea Actions CI/CD pipelines
- Docker container registry (`registry.lunarfront.tech`)

Managed by Ansible. See `lunarfront-infra/ansible/roles/gitea/`.

### CI Pipeline (Gitea Actions)

On push to `main`:

1. Run lint + unit tests using the shared `ci-runner` image
2. Build Docker image, push to `registry.lunarfront.tech/ryan/lunarfront-app:{sha}`
3. Update the Helm chart `values.yaml` with the new image tag
4. ArgoCD detects the change and syncs all customer deployments

---

## Kubernetes (DOKS + ArgoCD)

All customer app instances run on a single DOKS cluster managed by ArgoCD (GitOps).

### Repository Structure

```
/gitops
  apps/
    customer-acme/
      values.yaml
    customer-foo/
      values.yaml
  chart/
    Chart.yaml
    templates/
      deployment.yaml
      service.yaml
      ingress.yaml
      job-migrate.yaml   # runs drizzle-kit migrate on deploy
      secret.yaml
```

### Per-Customer values.yaml

```yaml
customer: acme
subdomain: acme

image:
  repository: registry.lunarfront.tech/ryan/lunarfront-app
  tag: "abc123"

database:
  url: "postgresql://lunarfront:pass@db-host:25060/customer_acme?sslmode=require"

redis:
  url: "rediss://..."

env:
  JWT_SECRET: "..."
  NODE_ENV: production
```

### Adding a New Customer

1. `CREATE DATABASE customer_x;` on the shared managed Postgres
2. Add `gitops/apps/customer-x/values.yaml`
3. Push — ArgoCD syncs, migration Job runs, instance is live
4. Add DNS CNAME in Terraform

---

## Database Strategy

One managed Postgres cluster shared across all customers. Each customer gets their own isolated database (`CREATE DATABASE customer_x`). No cross-customer queries are possible at the database level.

- New customer = new database, no new infrastructure cost
- Managed DO Postgres handles backups, failover, and SSL
- Resize the cluster as total load grows

---

## Day-to-Day Workflow

```bash
# Provision infrastructure from scratch
terraform init && terraform apply

# Configure Gitea server
ansible-playbook ansible/gitea.yml -i ansible/inventory.ini

# Normal deploy flow — push to main
git push origin main
# → Gitea Actions: lint + test + build image
# → Image pushed to registry
# → Helm chart values updated
# → ArgoCD syncs all customer deployments automatically

# Add a new customer
# 1. Create database
psql $DATABASE_URL -c "CREATE DATABASE customer_x;"
# 2. Add values file, push
git add gitops/apps/customer-x/values.yaml && git push
# Done — ArgoCD handles the rest
```
