feat: migrate Woodpecker CI from SQLite to Postgres (CNPG) #59

Merged
forgejo_admin merged 1 commit from 28-woodpecker-sqlite-to-postgres into main 2026-03-14 20:05:35 +00:00

Summary

Migrates Woodpecker CI from its default SQLite database to a CNPG-managed Postgres cluster. This resolves the known log streaming bug caused by SQLite limitations in the Kubernetes backend, and brings Woodpecker's data layer in line with the rest of the platform's Postgres infrastructure.

Changes

  • terraform/variables.tf -- Added woodpecker_db_password variable (sensitive)
  • terraform/k3s.tfvars.example -- Added woodpecker_db_password placeholder
  • terraform/main.tf -- Added 3 new resources:
    • kubernetes_secret_v1.woodpecker_cnpg_s3_creds -- S3 creds in woodpecker namespace for WAL archiving
    • kubernetes_secret_v1.woodpecker_db_credentials -- DB user/pass secret for CNPG bootstrap
    • kubernetes_manifest.woodpecker_postgres -- CNPG Cluster CR (1 instance, 2Gi storage, WAL to MinIO, PodMonitor)
  • terraform/main.tf -- Updated Woodpecker Helm release:
    • Added WOODPECKER_DATABASE_DRIVER=postgres and WOODPECKER_DATABASE_DATASOURCE env vars
    • Added kubernetes_manifest.woodpecker_postgres to depends_on
  • .woodpecker.yaml -- Added TF_VAR_woodpecker_db_password secret to both plan and apply CI steps

Test Plan

  • CI runs tofu fmt -check and tofu validate on this PR
  • tofu plan output posted as PR comment by CI
  • After merge: verify woodpecker-db pod is Running in woodpecker namespace
  • Verify Woodpecker server connects to Postgres (check server logs for DB driver)
  • Re-activate repos and verify a pipeline runs end-to-end

Review Checklist

  • Passed automated review-fix loop
  • No secrets committed
  • No unnecessary file changes
  • Commit messages are descriptive
  • DOWNTIME WARNING: Woodpecker server will restart with a fresh Postgres database. All pipeline history, secrets, and repo activations from SQLite will be lost. Re-activate repos and re-add CI secrets after apply.
  • Pre-merge: Add tf_var_woodpecker_db_password secret to Woodpecker CI for this repo
  • Pre-merge: Add woodpecker_db_password to local k3s.tfvars
  • Closes #28
  • Plan: plan-pal-e-platform
## Summary Migrates Woodpecker CI from its default SQLite database to a CNPG-managed Postgres cluster. This resolves the known log streaming bug caused by SQLite limitations in the Kubernetes backend, and brings Woodpecker's data layer in line with the rest of the platform's Postgres infrastructure. ## Changes - `terraform/variables.tf` -- Added `woodpecker_db_password` variable (sensitive) - `terraform/k3s.tfvars.example` -- Added `woodpecker_db_password` placeholder - `terraform/main.tf` -- Added 3 new resources: - `kubernetes_secret_v1.woodpecker_cnpg_s3_creds` -- S3 creds in woodpecker namespace for WAL archiving - `kubernetes_secret_v1.woodpecker_db_credentials` -- DB user/pass secret for CNPG bootstrap - `kubernetes_manifest.woodpecker_postgres` -- CNPG Cluster CR (1 instance, 2Gi storage, WAL to MinIO, PodMonitor) - `terraform/main.tf` -- Updated Woodpecker Helm release: - Added `WOODPECKER_DATABASE_DRIVER=postgres` and `WOODPECKER_DATABASE_DATASOURCE` env vars - Added `kubernetes_manifest.woodpecker_postgres` to `depends_on` - `.woodpecker.yaml` -- Added `TF_VAR_woodpecker_db_password` secret to both `plan` and `apply` CI steps ## Test Plan - [ ] CI runs `tofu fmt -check` and `tofu validate` on this PR - [ ] `tofu plan` output posted as PR comment by CI - [ ] After merge: verify `woodpecker-db` pod is Running in woodpecker namespace - [ ] Verify Woodpecker server connects to Postgres (check server logs for DB driver) - [ ] Re-activate repos and verify a pipeline runs end-to-end ## Review Checklist - [ ] Passed automated review-fix loop - [ ] No secrets committed - [ ] No unnecessary file changes - [ ] Commit messages are descriptive - [ ] **DOWNTIME WARNING**: Woodpecker server will restart with a fresh Postgres database. All pipeline history, secrets, and repo activations from SQLite will be lost. Re-activate repos and re-add CI secrets after apply. - [ ] **Pre-merge**: Add `tf_var_woodpecker_db_password` secret to Woodpecker CI for this repo - [ ] **Pre-merge**: Add `woodpecker_db_password` to local `k3s.tfvars` ## Related - Closes #28 - Plan: `plan-pal-e-platform`
feat: migrate Woodpecker CI from SQLite to Postgres (CNPG)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
39932c727f
- Add CNPG Cluster for Woodpecker (woodpecker-db) in woodpecker namespace
- Configure S3 WAL archiving to MinIO with gzip compression
- Add woodpecker_db_password variable and DB credentials secret
- Update Woodpecker Helm release with WOODPECKER_DATABASE_DRIVER=postgres
- Wire CI secrets for plan and apply steps
- Update k3s.tfvars.example with new variable

Closes #28

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
Owner

Review-Fix Loop -- Pass 1

Result: PASS -- no issues found.

Reviewed all 4 changed files (124 additions, 5 deletions):

  • No secrets committed -- k3s.tfvars is gitignored; only the .example file has a placeholder. Password variable is sensitive = true. Datasource uses interpolation, not hardcoded values.
  • CNPG Cluster CR -- Follows the existing pattern from the postgres namespace. Distinct WAL path (s3://postgres-wal/woodpecker/), same MinIO IAM user, same compression and storage class settings.
  • Dependency chain correct -- woodpecker_postgres depends on CNPG operator + both secrets. Woodpecker Helm release depends on Forgejo + the new CNPG cluster.
  • CI secrets wired -- TF_VAR_woodpecker_db_password added to both plan and apply steps, consistent placement.
  • Formatting verified -- tofu fmt -check passes clean.

No fixes needed. PR is ready for human review.

## Review-Fix Loop -- Pass 1 **Result: PASS -- no issues found.** Reviewed all 4 changed files (124 additions, 5 deletions): - **No secrets committed** -- `k3s.tfvars` is gitignored; only the `.example` file has a placeholder. Password variable is `sensitive = true`. Datasource uses interpolation, not hardcoded values. - **CNPG Cluster CR** -- Follows the existing pattern from the postgres namespace. Distinct WAL path (`s3://postgres-wal/woodpecker/`), same MinIO IAM user, same compression and storage class settings. - **Dependency chain correct** -- `woodpecker_postgres` depends on CNPG operator + both secrets. Woodpecker Helm release depends on Forgejo + the new CNPG cluster. - **CI secrets wired** -- `TF_VAR_woodpecker_db_password` added to both `plan` and `apply` steps, consistent placement. - **Formatting verified** -- `tofu fmt -check` passes clean. No fixes needed. PR is ready for human review.
Author
Owner

Review — Pass (with pre-merge checklist)

Files changed: 4 (+124 / -5)

Code Review

All changes are correct and well-structured:

  1. CNPG Cluster CR — Follows the existing pal-e-postgres pattern. Correct image (17.4-1), storage (2Gi local-path), backup to s3://postgres-wal/woodpecker/ with gzip compression, 7d retention, PodMonitor enabled. depends_on chain is correct: CNPG operator → secrets → Cluster → Helm release.

  2. S3 creds duplication — Necessary because CNPG looks for the secret in its own namespace. Reuses the shared minio_iam_user.cnpg IAM user which already has postgres-wal bucket access.

  3. DB credentials secret — Uses kubernetes.io/basic-auth type as required by CNPG bootstrap.

  4. Helm values updateWOODPECKER_DATABASE_DRIVER=postgres and datasource URL with sslmode=disable (fine for in-cluster). Env block alignment is cosmetic. No lines were lost — SKIP_VERIFY was already removed in PR #56.

  5. CI pipeline — Secret wired in both plan and apply steps. Variable definition is sensitive.

  6. No secrets committedk3s.tfvars.example has placeholder, not real password.

Risk Assessment

HIGH — Woodpecker downtime is expected. All SQLite data will be lost:

  • Pipeline history (acceptable — log streaming doesn't work anyway)
  • CI secrets (17+ per repo — need to be re-created)
  • Repo activations (need to be re-activated in Woodpecker UI)

Pre-Merge Checklist

  • Add woodpecker_db_password = "kM3L4AhLNiuMhIY7tMQ" to local k3s.tfvars
  • Add tf_var_woodpecker_db_password CI secret in Woodpecker for this repo
  • Document current CI secrets per repo (for re-creation after migration)
  • Schedule a maintenance window — all CI will be down during migration

Post-Merge Verification

  • kubectl get cluster -n woodpecker shows healthy
  • kubectl logs woodpecker-server-0 -n woodpecker shows postgres driver
  • Re-activate repos in Woodpecker UI
  • Re-add CI secrets for all repos
  • Trigger test pipeline — verify logs visible in UI
## Review — Pass (with pre-merge checklist) **Files changed:** 4 (+124 / -5) ### Code Review All changes are correct and well-structured: 1. **CNPG Cluster CR** — Follows the existing pal-e-postgres pattern. Correct image (17.4-1), storage (2Gi local-path), backup to `s3://postgres-wal/woodpecker/` with gzip compression, 7d retention, PodMonitor enabled. `depends_on` chain is correct: CNPG operator → secrets → Cluster → Helm release. 2. **S3 creds duplication** — Necessary because CNPG looks for the secret in its own namespace. Reuses the shared `minio_iam_user.cnpg` IAM user which already has `postgres-wal` bucket access. 3. **DB credentials secret** — Uses `kubernetes.io/basic-auth` type as required by CNPG bootstrap. 4. **Helm values update** — `WOODPECKER_DATABASE_DRIVER=postgres` and datasource URL with `sslmode=disable` (fine for in-cluster). Env block alignment is cosmetic. No lines were lost — `SKIP_VERIFY` was already removed in PR #56. 5. **CI pipeline** — Secret wired in both plan and apply steps. Variable definition is sensitive. 6. **No secrets committed** — `k3s.tfvars.example` has placeholder, not real password. ### Risk Assessment **HIGH** — Woodpecker downtime is expected. All SQLite data will be lost: - Pipeline history (acceptable — log streaming doesn't work anyway) - **CI secrets** (17+ per repo — need to be re-created) - **Repo activations** (need to be re-activated in Woodpecker UI) ### Pre-Merge Checklist - [ ] Add `woodpecker_db_password = "kM3L4AhLNiuMhIY7tMQ"` to local `k3s.tfvars` - [ ] Add `tf_var_woodpecker_db_password` CI secret in Woodpecker for this repo - [ ] Document current CI secrets per repo (for re-creation after migration) - [ ] Schedule a maintenance window — all CI will be down during migration ### Post-Merge Verification - [ ] `kubectl get cluster -n woodpecker` shows healthy - [ ] `kubectl logs woodpecker-server-0 -n woodpecker` shows postgres driver - [ ] Re-activate repos in Woodpecker UI - [ ] Re-add CI secrets for all repos - [ ] Trigger test pipeline — verify logs visible in UI
forgejo_admin deleted branch 28-woodpecker-sqlite-to-postgres 2026-03-14 20:05:35 +00:00
Sign in to join this conversation.
No description provided.