infra/.claude/skills/setup-project.md
Viktor Barzin c14dc88ffa [ci skip] Clean up .claude: remove remote executor and /remote skill references
All commands and skills now reference tools directly without any remote
execution wrapper. Archived setup-remote-executor.md for reference.
Added rule: all infra changes must go through Terraform.
2026-02-07 13:21:58 +00:00

387 lines
9.5 KiB
Markdown

# Setup Project Skill
**Purpose**: Deploy a new self-hosted service to the Kubernetes cluster from a GitHub repository.
**When to use**: User provides a GitHub URL or project name and wants to deploy it to the cluster.
## Workflow
### 1. Research Phase
**Input**: GitHub repository URL or project name
**Actions**:
- Visit the GitHub repository
- Check the README for:
- Official Docker image (Docker Hub, ghcr.io, etc.)
- docker-compose.yml file
- Self-hosting documentation
- Required dependencies (PostgreSQL, MySQL, Redis, etc.)
- Environment variables needed
- Default ports
- Storage requirements
**Find Docker Image Priority**:
1. Check official documentation for recommended image
2. Look in docker-compose.yml for `image:` directive
3. Check GitHub Container Registry: `ghcr.io/<org>/<repo>`
4. Check Docker Hub: `<org>/<repo>`
5. Check releases page for container images
6. Last resort: Build from Dockerfile (avoid if possible)
**Extract Configuration**:
- Container port (default port the app listens on)
- Environment variables (DATABASE_URL, REDIS_HOST, SMTP, etc.)
- Volume mounts (what data needs persistence)
- Dependencies (database type, cache, etc.)
### 2. Database Setup (if needed)
**If project requires PostgreSQL**:
- User provides database credentials or use pattern: `<service>` user with secure password
- Database will be created in shared `postgresql.dbaas.svc.cluster.local`
- Connection string format: `postgresql://<user>:<password>@postgresql.dbaas.svc.cluster.local:5432/<dbname>`
**If project requires MySQL**:
- User provides database credentials
- Database in shared `mysql.dbaas.svc.cluster.local`
- Connection string format: `mysql://<user>:<password>@mysql.dbaas.svc.cluster.local:3306/<dbname>`
**If project requires Redis**:
- Use shared Redis: `redis.redis.svc.cluster.local:6379`
- No password required
**IMPORTANT**: Never create databases yourself - always ask user for credentials to use.
### 3. Terraform Module Creation
**Create module directory**:
```bash
mkdir -p modules/kubernetes/<service-name>/
```
**Create `modules/kubernetes/<service-name>/main.tf`**:
```hcl
variable "tls_secret_name" {}
variable "tier" { type = string }
variable "postgresql_password" {} # Only if needed
# Add other variables as needed (smtp_password, api_keys, etc.)
resource "kubernetes_namespace" "<service>" {
metadata {
name = "<service>"
}
}
module "tls_secret" {
source = "../setup_tls_secret"
namespace = kubernetes_namespace.<service>.metadata[0].name
tls_secret_name = var.tls_secret_name
}
# If database migrations needed, add init_container
resource "kubernetes_deployment" "<service>" {
metadata {
name = "<service>"
namespace = kubernetes_namespace.<service>.metadata[0].name
labels = {
app = "<service>"
tier = var.tier
}
}
spec {
replicas = 1
selector {
match_labels = {
app = "<service>"
}
}
template {
metadata {
labels = {
app = "<service>"
}
}
spec {
# Init container for migrations (if needed)
# init_container { ... }
container {
name = "<service>"
image = "<docker-image>:<tag>"
port {
container_port = <port>
}
# Environment variables
env {
name = "DATABASE_URL"
value = "postgresql://<service>:${var.postgresql_password}@postgresql.dbaas.svc.cluster.local:5432/<service>"
}
# Add other env vars as needed
# Volume mounts for persistent data
volume_mount {
name = "data"
mount_path = "<mount-path>"
sub_path = "<optional-subpath>"
}
resources {
requests = {
memory = "256Mi"
cpu = "100m"
}
limits = {
memory = "2Gi"
cpu = "1"
}
}
# Health checks (if endpoints exist)
liveness_probe {
http_get {
path = "/health" # or /healthz, /, etc.
port = <port>
}
initial_delay_seconds = 60
period_seconds = 30
}
}
# NFS volume for persistence
volume {
name = "data"
nfs {
server = "10.0.10.15"
path = "/mnt/main/<service>"
}
}
}
}
}
}
resource "kubernetes_service" "<service>" {
metadata {
name = "<service>"
namespace = kubernetes_namespace.<service>.metadata[0].name
labels = {
app = "<service>"
}
}
spec {
selector = {
app = "<service>"
}
port {
name = "http"
port = 80
target_port = <container-port>
}
}
}
module "ingress" {
source = "../ingress_factory"
namespace = kubernetes_namespace.<service>.metadata[0].name
name = "<service>"
tls_secret_name = var.tls_secret_name
# Add extra_annotations if needed (proxy-body-size, timeouts, etc.)
}
```
### 4. Update Main Terraform Files
**Add to `modules/kubernetes/main.tf`**:
1. Add variable declarations at top:
```hcl
variable "<service>_postgresql_password" { type = string }
```
2. Add to appropriate DEFCON level (ask user which level, default to 5):
```hcl
5 : [
...,
"<service>"
]
```
3. Add module block at bottom:
```hcl
module "<service>" {
source = "./<service>"
for_each = contains(local.active_modules, "<service>") ? { <service> = true } : {}
tls_secret_name = var.tls_secret_name
postgresql_password = var.<service>_postgresql_password
tier = local.tiers.aux # or appropriate tier
depends_on = [null_resource.core_services]
}
```
**Add to `main.tf`**:
1. Add variable:
```hcl
variable "<service>_postgresql_password" { type = string }
```
2. Pass to kubernetes_cluster module:
```hcl
module "kubernetes_cluster" {
...
<service>_postgresql_password = var.<service>_postgresql_password
}
```
**Update `terraform.tfvars`**:
1. Add password/credentials:
```hcl
<service>_postgresql_password = "<secure-password>"
```
2. Add to Cloudflare DNS (ask user if proxied or non-proxied):
```hcl
cloudflare_non_proxied_names = [
...,
"<service>"
]
```
### 5. Email/SMTP Configuration (if needed)
If service needs to send emails:
```hcl
env {
name = "MAILER_HOST"
value = "mailserver.viktorbarzin.me" # Public hostname for TLS
}
env {
name = "MAILER_PORT"
value = "587"
}
env {
name = "MAILER_USER"
value = "info@viktorbarzin.me"
}
env {
name = "MAILER_PASSWORD"
value = var.mailserver_accounts["info@viktorbarzin.me"] # Pass from module
}
```
Add to module call:
```hcl
smtp_password = var.mailserver_accounts["info@viktorbarzin.me"]
```
### 6. Apply Terraform
```bash
terraform init
terraform apply -target=module.kubernetes_cluster.module.<service> -auto-approve
```
### 7. Verification
```bash
kubectl get pods -n <service>
kubectl logs -n <service> -l app=<service> --tail=50
```
Test URL: `https://<service>.viktorbarzin.me`
### 8. Commit Changes
```bash
git add modules/kubernetes/<service>/ main.tf modules/kubernetes/main.tf terraform.tfvars
git commit -m "Add <service> deployment
- Deploy <service> as <description>
- Uses <dependencies>
- Ingress at <service>.viktorbarzin.me
[ci skip]"
```
## Common Patterns
### Init Container for Migrations
```hcl
init_container {
name = "migration"
image = "<same-image>"
command = ["sh", "-c", "<migration-command>"]
# Same env vars and volumes as main container
}
```
### Dynamic Environment Variables
```hcl
locals {
common_env = [
{ name = "VAR1", value = "value1" },
{ name = "VAR2", value = "value2" },
]
}
dynamic "env" {
for_each = local.common_env
content {
name = env.value.name
value = env.value.value
}
}
```
### External URL Configuration
Many apps need their public URL configured:
```hcl
env {
name = "APP_URL" # or PUBLIC_URL, EXTERNAL_URL, etc.
value = "https://<service>.viktorbarzin.me"
}
env {
name = "HTTPS" # or ENABLE_HTTPS, etc.
value = "true"
}
```
## Checklist
- [ ] Find official Docker image or docker-compose
- [ ] Identify dependencies (DB, Redis, etc.)
- [ ] Ask user for database credentials (never create yourself)
- [ ] Create `modules/kubernetes/<service>/main.tf`
- [ ] Update `modules/kubernetes/main.tf` (variables, DEFCON level, module block)
- [ ] Update `main.tf` (variable, pass to module)
- [ ] Update `terraform.tfvars` (password, Cloudflare DNS)
- [ ] Run `terraform init` and `terraform apply`
- [ ] Verify pods are running
- [ ] Test the URL
- [ ] Commit changes with `[ci skip]`
## Questions to Ask User
1. What DEFCON level should this service be in? (Default: 5)
2. Should Cloudflare proxy this domain? (Default: no, add to non_proxied_names)
3. Does this need email/SMTP? (Configure if yes)
4. What database credentials should I use? (Never create yourself)
5. What tier? (core/cluster/gpu/edge/aux - default: aux)
## Notes
- **Always use official documentation** as the source of truth
- **Prefer stable/latest tags** over specific versions for self-hosted
- **Use shared infrastructure**: PostgreSQL at `postgresql.dbaas.svc.cluster.local`, Redis at `redis.redis.svc.cluster.local`
- **NFS storage**: Always at `10.0.10.15:/mnt/main/<service>`
- **Email**: Use `mailserver.viktorbarzin.me` (public hostname) not internal service name
- **Resource limits**: Start conservative, can increase if needed
- **Health checks**: Only add if the app has health endpoints