Skip to content

Bug fixes and improvements#5

Merged
clofour merged 10 commits intomainfrom
dev
Apr 19, 2026
Merged

Bug fixes and improvements#5
clofour merged 10 commits intomainfrom
dev

Conversation

@clofour
Copy link
Copy Markdown
Owner

@clofour clofour commented Apr 13, 2026

No description provided.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8d6ad71-8166-42a4-9535-8e5962791247

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Migrates ClusterIssuer provisioning from a static Kubernetes manifest to a Helm chart and Helm release, adds external-dns Helm release and Terraform time provider, updates Helm/GitLab values and Terraform resource wiring (DNS, Postgres, namespaces, secrets), and enables ignoring Terraform plan files.

Changes

Cohort / File(s) Summary
Gitignore
\.gitignore
Uncommented the *tfplan* pattern so Terraform plan files are ignored.
Cluster-issuer Helm chart
helm/cluster-issuer/chart/.helmignore, helm/cluster-issuer/chart/Chart.yaml, helm/cluster-issuer/chart/templates/_helpers.tpl, helm/cluster-issuer/chart/templates/cluster-issuer.yaml, helm/cluster-issuer/chart/values.yaml
Added a full Helm chart for cluster-issuer: chart metadata, packaging ignore, template helpers, ClusterIssuer template, and default ACME values.
Top-level Helm values
helm/cluster-issuer/values.yaml, helm/gitlab/values.yaml
Added cluster-issuer email placeholder; reworked GitLab values (removed backups bucket keys, added global.minio.enabled, switched dependency flag from minio.installgitlab-runner.install, renamed certmanager.installinstallCertmanager).
External DNS Helm values
helm/external-dns/values.yaml
Added external-dns values using a webhook provider, secret-based DO_TOKEN, hardened securityContext, and health probes.
Ingress NGINX values
helm/ingress-nginx/values.yaml
Disabled PROXY protocol and removed the DO load balancer proxy annotation.
Removed static manifest
kubernetes/cluster-issuer.yaml
Deleted the static ClusterIssuer manifest (now provisioned via Helm chart).
Terraform: providers & lockfile
terraform/versions.tf, terraform/.terraform.lock.hcl
Added hashicorp/time provider requirement and corresponding lockfile entry.
Terraform: Helm releases & sequencing
terraform/helm.tf
Reworked Helm releases: reintroduced ingress_nginx, added external_dns and cluster_issuer releases, adjusted gitlab release values, added long timeouts/waits, and updated depends_on lists.
Terraform: Kubernetes resources & sequencing
terraform/kubernetes.tf
Added external-dns namespace and secret, removed kubernetes_manifest.cluster_issuer, changed postgres secret password source, added time_sleep.wait_for_lb and switched service lookup to depend on the sleep resource, disabled gitlab S3 secret.
Terraform: DNS record fix
terraform/dns.tf
Made digitalocean_record.gitlab unconditional (count = 1) and corrected value to use local.lb_ip.
Terraform: Postgres & connection pooling
terraform/postgres.tf
Downgraded Postgres version from 18 → 17, added postgresql config (max_locks_per_transaction = 256) and a digitalocean_database_connection_pool resource.
Terraform: Spaces buckets
terraform/spaces.tf
Removed/commented several bucket entries from locals.buckets, reducing created Spaces buckets.
Terraform: variables
terraform/variables.tf
Added sensitive do_dns_token variable; changed defaults: region fra1 → ams3, node_size s-2vcpu-4gb → s-4vcpu-8gb.

Sequence Diagram

sequenceDiagram
    participant TF as Terraform
    participant Helm as Helm
    participant K8s as Kubernetes API
    participant CM as cert-manager
    participant DNS as DigitalOcean DNS

    TF->>Helm: deploy cert-manager release
    Helm->>K8s: create cert-manager resources
    K8s->>CM: cert-manager initializes

    TF->>Helm: deploy cluster-issuer (depends_on cert-manager)
    Helm->>K8s: create ClusterIssuer resource
    K8s->>CM: register ClusterIssuer

    TF->>Helm: deploy ingress-nginx
    Helm->>K8s: create ingress-nginx service (LoadBalancer)

    TF->>TF: time_sleep.wait_for_lb (120s)
    TF->>K8s: query ingress-nginx service for LB IP
    K8s-->>TF: return LB IP

    TF->>DNS: create/update DNS record with LB IP
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author, making it impossible to assess whether the description relates to the changeset. Provide a detailed pull request description explaining the purpose, motivation, and key changes made in this PR.
Title check ❓ Inconclusive The title 'Bug fixes and improvements' is extremely vague and generic, using non-descriptive terms that fail to convey what the changeset actually addresses or modifies. Replace the generic title with a specific description of the main changes, such as 'Add cluster-issuer Helm chart and external-DNS integration' or similar that reflects the primary focus.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 34d071bd-d435-43bb-9c6c-0f16346c708e

📥 Commits

Reviewing files that changed from the base of the PR and between 2bc2e3f and 777dc24.

📒 Files selected for processing (14)
  • .gitignore
  • helm/cluster-issuer/chart/.helmignore
  • helm/cluster-issuer/chart/Chart.yaml
  • helm/cluster-issuer/chart/templates/_helpers.tpl
  • helm/cluster-issuer/chart/templates/cluster-issuer.yaml
  • helm/cluster-issuer/chart/values.yaml
  • helm/cluster-issuer/values.yaml
  • helm/gitlab/values.yaml
  • kubernetes/cluster-issuer.yaml
  • terraform/.terraform.lock.hcl
  • terraform/dns.tf
  • terraform/helm.tf
  • terraform/kubernetes.tf
  • terraform/versions.tf
💤 Files with no reviewable changes (1)
  • kubernetes/cluster-issuer.yaml
📜 Review details
🧰 Additional context used
🪛 YAMLlint (1.38.0)
helm/cluster-issuer/chart/templates/cluster-issuer.yaml

[error] 4-4: too many spaces inside braces

(braces)


[error] 4-4: too many spaces inside braces

(braces)


[error] 7-7: too many spaces inside braces

(braces)


[error] 7-7: too many spaces inside braces

(braces)


[error] 8-8: too many spaces inside braces

(braces)


[error] 8-8: too many spaces inside braces

(braces)


[error] 10-10: too many spaces inside braces

(braces)


[error] 10-10: too many spaces inside braces

(braces)


[error] 14-14: too many spaces inside braces

(braces)


[error] 14-14: too many spaces inside braces

(braces)

🔇 Additional comments (14)
.gitignore (1)

38-38: Good ignore rule for Terraform plan artifacts.

This correctly prevents plan output files from being committed.

helm/cluster-issuer/chart/.helmignore (1)

1-23: Solid packaging hygiene defaults.

The ignore set is appropriate for keeping chart packages clean and reproducible.

helm/gitlab/values.yaml (1)

66-67: Configuration changes are consistent with the new deployment model.

These values align with disabling in-chart components while relying on Terraform-managed cert-manager and issuer resources.

Also applies to: 79-80, 85-85

helm/cluster-issuer/chart/templates/_helpers.tpl (1)

4-62: Helpers look clean and idiomatic.

The naming/label helpers are well-structured and reusable.

helm/cluster-issuer/values.yaml (1)

1-1: Templated email input looks correct.

This keeps issuer contact configuration environment-driven.

terraform/versions.tf (1)

21-24: Provider addition is appropriate.

Adding hashicorp/time is consistent with the new orchestration dependency.

helm/cluster-issuer/chart/values.yaml (1)

1-5: Default chart values are coherent.

The ACME and solver defaults are well-formed for initial chart rendering.

terraform/dns.tf (1)

10-15: The readiness gating is already properly implemented.

local.lb_ip uses try(..., null) and the count on the digitalocean_record resource is already conditionally set to count = local.lb_ip != null ? 1 : 0, which prevents creation until the ingress controller's load balancer IP is available. No changes needed.

			> Likely an incorrect or invalid review comment.
terraform/.terraform.lock.hcl (1)

87-106: LGTM!

The lock entry for hashicorp/time v0.13.1 is correctly added and aligns with the provider declaration in terraform/versions.tf. This supports the new time_sleep.wait_for_lb resource in kubernetes.tf.

helm/cluster-issuer/chart/templates/cluster-issuer.yaml (1)

1-14: LGTM!

The ClusterIssuer template is correctly structured for cert-manager's ACME HTTP-01 challenge workflow. All referenced values (.Values.name, .Values.server, .Values.email, .Values.privateKeySecretName, .Values.ingressClass) are properly defined in helm/cluster-issuer/chart/values.yaml.

Note: The YAMLlint errors about "too many spaces inside braces" are false positives—the {{ .Values.* }} syntax with spaces is standard Go/Helm templating convention.

terraform/kubernetes.tf (1)

138-138: LGTM!

The dependency chain is correctly updated: helm_release.ingress_nginxtime_sleep.wait_for_lbkubernetes_service_v1 data source lookup. This ensures the service query occurs after the wait period.

terraform/helm.tf (3)

1-11: LGTM!

The cert-manager Helm release is correctly configured with the OCI registry path and version 1.20.2.


49-51: LGTM!

The timeout = 1800 (30 minutes), wait = true, and wait_for_jobs = true settings are appropriate for the GitLab Helm chart, which is large and has multiple jobs that need to complete during installation.


68-79: LGTM!

The expanded depends_on list correctly captures all prerequisite resources: database clusters/users, secrets, cluster-issuer, and DNS records. This ensures GitLab installation only proceeds when all dependencies are ready.

# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider aligning appVersion with cert-manager or removing it.

The appVersion: "1.16.0" doesn't correspond to the cert-manager version (1.20.2 per helm.tf) being deployed. Since this chart deploys a ClusterIssuer CRD instance rather than a versioned application, consider either updating to match cert-manager's version or omitting appVersion entirely.

Comment thread terraform/helm.tf
Comment thread terraform/helm.tf Outdated
values = [
file("${path.module}/../helm/cert-manager/values.yaml")
]
depends_on = [helm_release.cert_manager]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Unnecessary dependency: ingress-nginx doesn't require cert-manager.

ingress-nginx and cert-manager are independent components that can be installed in parallel. This dependency adds unnecessary sequencing to deployments. Consider removing it unless there's a specific ordering requirement.

♻️ Remove unnecessary dependency
 resource "helm_release" "ingress_nginx" {
     name = "ingress-nginx"
     ...
-    depends_on = [helm_release.cert_manager]
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
depends_on = [helm_release.cert_manager]

Comment thread terraform/kubernetes.tf
Comment on lines +127 to +130
resource "time_sleep" "wait_for_lb" {
depends_on = [ helm_release.ingress_nginx ]
create_duration = "120s"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Hardcoded 120s wait duration may be fragile.

The time_sleep is a pragmatic workaround for the LoadBalancer IP assignment race condition, but the 120-second duration is a magic number that:

  • May not be sufficient if DigitalOcean LB provisioning is slow
  • Unnecessarily delays deployments when the LB is ready sooner

Consider adding a comment explaining why 120s was chosen, or consider a more robust approach using a null_resource with a retry loop that polls for the LB IP.

📝 Suggested documentation
 resource "time_sleep" "wait_for_lb" {
+    # Wait for DigitalOcean to provision the LoadBalancer and assign an external IP.
+    # 120s is typically sufficient for DO LB provisioning; adjust if timeouts occur.
     depends_on = [ helm_release.ingress_nginx ]
     create_duration = "120s"
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resource "time_sleep" "wait_for_lb" {
depends_on = [ helm_release.ingress_nginx ]
create_duration = "120s"
}
resource "time_sleep" "wait_for_lb" {
# Wait for DigitalOcean to provision the LoadBalancer and assign an external IP.
# 120s is typically sufficient for DO LB provisioning; adjust if timeouts occur.
depends_on = [ helm_release.ingress_nginx ]
create_duration = "120s"
}

@clofour
Copy link
Copy Markdown
Owner Author

clofour commented Apr 18, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
terraform/dns.tf (1)

1-16: ⚠️ Potential issue | 🟠 Major

Unconditional count = 1 combined with try(..., null) will apply a null value.

local.lb_ip uses try(data.kubernetes_service_v1.ingress_nginx.status[0].load_balancer[0].ingress[0].ip, null). Previously the conditional count guarded against a null result; now that count = 1 is unconditional, if the LB IP is not yet populated (e.g., time_sleep.wait_for_lb expires before DigitalOcean assigns the IP, or the status path is momentarily missing), Terraform will attempt to create a DNS A record with value = null and fail with a confusing provider error instead of surfacing the root cause.

Either drop the null fallback so Terraform fails fast on the data source with a clear message, or reintroduce a precondition:

🛠 Proposed fix
 locals {
-    lb_ip = try(data.kubernetes_service_v1.ingress_nginx.status[0].load_balancer[0].ingress[0].ip, null)
+    lb_ip = data.kubernetes_service_v1.ingress_nginx.status[0].load_balancer[0].ingress[0].ip
 }

Or add a lifecycle precondition on digitalocean_record.gitlab:

lifecycle {
  precondition {
    condition     = local.lb_ip != null
    error_message = "ingress-nginx LoadBalancer IP is not yet assigned; increase time_sleep.wait_for_lb."
  }
}
helm/gitlab/values.yaml (1)

63-82: ⚠️ Potential issue | 🔴 Critical

Use certmanager.install: false instead of installCertmanager: false to properly disable the bundled cert-manager subchart.

The top-level installCertmanager: false is not a recognized chart value and will be silently ignored, causing the bundled cert-manager to install alongside your own helm_release.cert_manager, potentially racing for CRDs and ClusterIssuers.

The correct key is certmanager.install: false (the existing global.ingress.configureCertmanager: false is also correct).

🛠 Proposed fix
 nginx-ingress:
   enabled: false
-installCertmanager: false
+certmanager:
+  install: false
♻️ Duplicate comments (1)
terraform/kubernetes.tf (1)

148-160: ⚠️ Potential issue | 🟠 Major

Replace the fixed sleep with an actual LoadBalancer IP guard.

A 120s sleep still doesn’t guarantee status.loadBalancer.ingress[0].ip exists. Since terraform/dns.tf now creates digitalocean_record.gitlab unconditionally from local.lb_ip, a slow DigitalOcean LB can leave the DNS record with a null/invalid value or fail the apply. Prefer active polling/readiness or restore a guard around DNS creation.

Safer DNS guard option
# terraform/dns.tf
 resource "digitalocean_record" "gitlab" {
-    count = 1
+    count = local.lb_ip != null ? 1 : 0

     domain = digitalocean_domain.main.name
     type = "A"
     name = var.gitlab_host
     value = local.lb_ip
     ttl = 300
 }
Terraform time provider time_sleep create_duration behavior when only depends_on resources update; Kubernetes LoadBalancer IP readiness patterns in Terraform

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7ec81fc8-6be2-4972-9112-09e2a72b2263

📥 Commits

Reviewing files that changed from the base of the PR and between 2bc2e3f and 62891a0.

📒 Files selected for processing (19)
  • .gitignore
  • helm/cluster-issuer/chart/.helmignore
  • helm/cluster-issuer/chart/Chart.yaml
  • helm/cluster-issuer/chart/templates/_helpers.tpl
  • helm/cluster-issuer/chart/templates/cluster-issuer.yaml
  • helm/cluster-issuer/chart/values.yaml
  • helm/cluster-issuer/values.yaml
  • helm/external-dns/values.yaml
  • helm/gitlab/values.yaml
  • helm/ingress-nginx/values.yaml
  • kubernetes/cluster-issuer.yaml
  • terraform/.terraform.lock.hcl
  • terraform/dns.tf
  • terraform/helm.tf
  • terraform/kubernetes.tf
  • terraform/postgres.tf
  • terraform/spaces.tf
  • terraform/variables.tf
  • terraform/versions.tf
💤 Files with no reviewable changes (1)
  • kubernetes/cluster-issuer.yaml
📜 Review details
🧰 Additional context used
🪛 YAMLlint (1.38.0)
helm/cluster-issuer/chart/templates/cluster-issuer.yaml

[error] 4-4: too many spaces inside braces

(braces)


[error] 4-4: too many spaces inside braces

(braces)


[error] 7-7: too many spaces inside braces

(braces)


[error] 7-7: too many spaces inside braces

(braces)


[error] 8-8: too many spaces inside braces

(braces)


[error] 8-8: too many spaces inside braces

(braces)


[error] 10-10: too many spaces inside braces

(braces)


[error] 10-10: too many spaces inside braces

(braces)


[error] 14-14: too many spaces inside braces

(braces)


[error] 14-14: too many spaces inside braces

(braces)

🔇 Additional comments (14)
helm/cluster-issuer/chart/Chart.yaml (1)

24-24: appVersion still doesn't reflect the deployed cert-manager version.

The chart renders a cert-manager.io/v1 ClusterIssuer for cert-manager 1.20.2 (per terraform/helm.tf), but appVersion: "1.16.0" doesn't track that. Align it with cert-manager, or drop appVersion since this chart only ships a CRD instance.

terraform/helm.tf (2)

25-38: cluster-issuer values.yaml still needs ${email} interpolation for this templatefile call to take effect.

The templatefile(.../cluster-issuer/values.yaml, { email = var.email }) call on line 31 only substitutes if the values file uses ${email}. If it still contains a literal email: webmaster@example.com, var.email is silently ignored.


40-50: LGTM — prior unnecessary depends_on on cert_manager was correctly dropped.

ingress-nginx and cert-manager can install in parallel; removing the ordering constraint here is the right call.

terraform/versions.tf (1)

21-24: LGTM!

The hashicorp/time provider addition is consistent with the new time_sleep.wait_for_lb usage in terraform/kubernetes.tf and the new lock entry in .terraform.lock.hcl.

terraform/.terraform.lock.hcl (1)

88-106: LGTM — auto-generated lock entry matches declared constraint.

The hashicorp/time lock entry (version = "0.13.1", constraints = "~> 0.13.1") is consistent with terraform/versions.tf.

terraform/variables.tf (2)

6-9: LGTM — separate scoped token for ExternalDNS.

Using a dedicated do_dns_token (instead of reusing var.do_token) is good least-privilege practice — the ExternalDNS webhook only needs DNS-scoped permissions.


22-40: Verify default changes are intentional and documented.

Changing region (fra1ams3) and node_size (s-2vcpu-4gbs-4vcpu-8gb) defaults will silently force a cluster rebuild and ~2× droplet cost for anyone not overriding these. Confirm this is intended, and consider noting the change in the repo README/CHANGELOG so existing consumers aren't surprised on the next terraform apply.

terraform/postgres.tf (2)

4-4: Confirm engine version rollback 18 → 17 is intentional.

Downgrading a running DO managed Postgres cluster is not supported in-place — if a cluster was previously provisioned at 18, this change will fail the plan or force a destroy/recreate (data loss). If this is a net-new deployment, ignore; otherwise plan a managed migration path. GitLab 17.x (chart 9.10.3) is tested against PG 14–16; PG 17 support was added in recent GitLab versions — verify compatibility with your chart version.


11-14: LGTM — max_locks_per_transaction = 256 is the documented GitLab requirement.

Good addition; GitLab recommends raising this from the PG default of 64 to avoid lock-table exhaustion during large backup/restore and schema operations.

terraform/kubernetes.tf (4)

1-7: External DNS namespace wiring looks good.

The namespace is explicitly ordered after the cluster and is referenced by the new external-dns secret.


37-48: The configuration is correct. The Helm chart in helm/external-dns/values.yaml (lines 7-11) explicitly maps the DO_TOKEN environment variable to the password key in the external-dns-do-secret secret, exactly as defined in the Terraform code. No action required.


109-126: No active references found to the registry S3 secret.

The search found zero active references to gitlab-s3-registry-secret in the configuration. The object storage in helm/gitlab/values.yaml is configured with gitlab-s3-main-secret, not the commented-out registry secret. Commenting out the resource is safe and will not cause registry pod failures.

			> Likely an incorrect or invalid review comment.

69-71: No action needed—username and password are already correctly paired.

GitLab's database connection uses both the username and password from the cluster's default identity (digitalocean_database_cluster.postgres.user and .password), so they originate from the same source. Authentication will not fail.

Note: A dedicated gitlab database user is created but remains unused; this is wasteful but not a functional problem.

			> Likely an incorrect or invalid review comment.
helm/cluster-issuer/chart/templates/_helpers.tpl (1)

1-62: Helm helper templates look good.

The naming, label, selector-label, and service account helper definitions follow standard Helm chart conventions and should compose cleanly with chart templates.

Comment thread helm/cluster-issuer/chart/values.yaml Outdated
Comment thread helm/external-dns/values.yaml
Comment thread terraform/helm.tf
Comment thread terraform/helm.tf
Comment thread terraform/postgres.tf
Comment on lines +26 to +32
resource "digitalocean_database_connection_pool" "main" {
cluster_id = digitalocean_database_cluster.postgres.id
name = "gitlab"
mode = "transaction"
size = 10
db_name = digitalocean_database_cluster.postgres.database
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cd /tmp && find . -type f -name "*.tf" | head -20

Repository: clofour/gitlab-kubernetes

Length of output: 51


🏁 Script executed:

git ls-files | grep -E "\.tf$"

Repository: clofour/gitlab-kubernetes

Length of output: 316


🏁 Script executed:

pwd

Repository: clofour/gitlab-kubernetes

Length of output: 87


🏁 Script executed:

find . -type f -name "postgres.tf" -o -name "helm.tf"

Repository: clofour/gitlab-kubernetes

Length of output: 112


🏁 Script executed:

cat terraform/postgres.tf

Repository: clofour/gitlab-kubernetes

Length of output: 1198


🏁 Script executed:

cat terraform/helm.tf

Repository: clofour/gitlab-kubernetes

Length of output: 3056


🌐 Web query:

DigitalOcean Terraform provider digitalocean_database_cluster database attribute

💡 Result:

In the DigitalOcean Terraform provider, the digitalocean_database_cluster resource exports a read-only attribute named database. This attribute provides the name of the cluster's default database (typically "defaultdb" for new clusters). It is available after cluster creation and can be referenced in other resources or outputs. It is listed in the official Attributes Reference section of both the resource and data source documentation: - Resource: database - Name of the cluster's default database. - Data source: Same attribute for querying existing clusters. Note that database is not a configurable argument; the cluster creates a default database automatically. Additional databases can be created using the separate digitalocean_database_db resource, which requires cluster_id and name arguments. Example usage: resource "digitalocean_database_cluster" "example" { name = "example-cluster" engine = "pg" version = "15" size = "db-s-1vcpu-1gb" region = "nyc1" node_count = 1 } output "default_database" { value = digitalocean_database_cluster.example.database }

Citations:


🏁 Script executed:

grep -r "database" terraform/ | grep -i "default\|cluster"

Repository: clofour/gitlab-kubernetes

Length of output: 1763


🌐 Web query:

DigitalOcean PgBouncer connection pool db_name routing behavior PostgreSQL

💡 Result:

In DigitalOcean Managed PostgreSQL Databases, PgBouncer connection pools are configured for a specific target database (specified by the "db" or "db_name" parameter when creating the pool) and user. Each pool provides dedicated backend connections exclusively to that one target database within the cluster. Clients must connect to a specific pool using its unique endpoint, host, and port (typically ending in 2506x). The connection string provided in the pool's "Connection details" has "database" set to the pool name (e.g., postgres://doadmin:pass@pool-host:25061/my-pool?...), but PgBouncer ignores the client-supplied db_name in the connection string. Instead, it always routes all connections through that pool to the fixed target database configured for the pool (e.g., "defaultdb"). There is no support for routing to different databases based on the db_name in the connection string within a single pool. To access a different database, create a separate connection pool configured for that database and connect clients to that pool's endpoint instead. This design allows allocating specific connection limits per database. PgBouncer pooling modes (transaction, session, statement) affect connection reuse but not database routing, which remains fixed per pool. Note: In transaction mode (default), avoid prepared statements, pg_dump, or session-state features like advisory locks, as connections are recycled between transactions.

Citations:


🌐 Web query:

DigitalOcean digitalocean_database_connection_pool db_name parameter does PgBouncer override client dbname

💡 Result:

No, PgBouncer in DigitalOcean's database connection pools does not override the client-specified dbname. Clients connect to the pool using a connection string where the database parameter is set to the pool's name (e.g., "postgres://doadmin:pass@host:25061/my_pool"). The pool is configured with a specific backend database via the db_name (or db) parameter during creation (e.g., "defaultdb"), which determines the target database the pool connects to. The client dbname selects which pool to connect to, but once connected, the pool proxies to the fixed backend database specified at pool creation. Clients cannot override the backend db_name to connect to a different database through the same pool; a separate pool must be created for each target database.

Citations:


🏁 Script executed:

grep -A 10 "resource \"digitalocean_database_connection_pool\"" terraform/postgres.tf

Repository: clofour/gitlab-kubernetes

Length of output: 434


Critical: connection pool routes to the wrong database (defaultdb, not gitlab).

On digitalocean_database_cluster, the database attribute refers to the cluster's default database — defaultdb — not the gitlab database created via digitalocean_database_db.gitlab. As written, PgBouncer will forward all sessions from this pool to defaultdb, ignoring any client-supplied database specification.

GitLab's Helm values set postgres_database = digitalocean_database_db.gitlab.name, expecting connections to the gitlab database. However, DigitalOcean connection pools are fixed to a single backend database at the PgBouncer level — the pool's db_name determines the actual target and cannot be overridden by the client. Result: GitLab will connect to defaultdb instead of gitlab, causing migrations and database initialization to fail.

Fix: bind the pool to the gitlab database and optionally specify the user:

 resource "digitalocean_database_connection_pool" "main" {
     cluster_id = digitalocean_database_cluster.postgres.id
     name = "gitlab"
     mode = "transaction"
     size = 10
-    db_name = digitalocean_database_cluster.postgres.database
+    db_name = digitalocean_database_db.gitlab.name
+    user    = digitalocean_database_user.gitlab.name
 }

Setting user explicitly binds the pool to the gitlab role for additional clarity and isolation; without it, the pool uses the inbound authenticating user (currently doadmin via Helm), which works but is less explicit.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resource "digitalocean_database_connection_pool" "main" {
cluster_id = digitalocean_database_cluster.postgres.id
name = "gitlab"
mode = "transaction"
size = 10
db_name = digitalocean_database_cluster.postgres.database
}
resource "digitalocean_database_connection_pool" "main" {
cluster_id = digitalocean_database_cluster.postgres.id
name = "gitlab"
mode = "transaction"
size = 10
db_name = digitalocean_database_db.gitlab.name
user = digitalocean_database_user.gitlab.name
}

Comment thread terraform/spaces.tf
Comment on lines 2 to 6
buckets = toset([
"artifacts", "lfs", "uploads", "packages",
"registry", "backups", "tmp", "ci-secure-files",
"dependency-proxy", "terraform-state", "pages"
# "registry", "backups", "tmp", # "ci-secure-files",
# "dependency-proxy", # "terraform-state", "pages"
])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP -C2 '\b(registry|backups)\b' helm/gitlab/values.yaml terraform/

Repository: clofour/gitlab-kubernetes

Length of output: 1485


🏁 Script executed:

cat -n helm/gitlab/values.yaml | head -50

Repository: clofour/gitlab-kubernetes

Length of output: 1370


🏁 Script executed:

rg -nP '(registry\.enabled|global\.hosts)' helm/gitlab/values.yaml

Repository: clofour/gitlab-kubernetes

Length of output: 51


🏁 Script executed:

rg -nP 'registry' helm/gitlab/values.yaml | head -20

Repository: clofour/gitlab-kubernetes

Length of output: 127


🏁 Script executed:

wc -l helm/gitlab/values.yaml

Repository: clofour/gitlab-kubernetes

Length of output: 95


🏁 Script executed:

rg -nP 'registry' helm/gitlab/values.yaml

Repository: clofour/gitlab-kubernetes

Length of output: 127


🏁 Script executed:

rg -nP 'enabled' helm/gitlab/values.yaml | grep -i registry

Repository: clofour/gitlab-kubernetes

Length of output: 51


🏁 Script executed:

cat -n helm/gitlab/values.yaml

Repository: clofour/gitlab-kubernetes

Length of output: 2114


🏁 Script executed:

sed -n '109,123p' terraform/kubernetes.tf

Repository: clofour/gitlab-kubernetes

Length of output: 614


Disable the registry service explicitly or restore its S3 bucket and secret.

The registry bucket and S3 secret are commented out in terraform/spaces.tf and terraform/kubernetes.tf, but helm/gitlab/values.yaml still configures global.hosts.registry.name and does not explicitly disable the registry subchart (which defaults to enabled in the GitLab Helm chart). This creates an inconsistent state: the registry pod will be scheduled but will fail to mount object storage since the bucket no longer exists.

To resolve, either:

  1. Explicitly disable registry in the Helm values (registry.enabled: false and remove global.hosts.registry), following the same pattern used for other disabled services (postgresql, redis, gitlab-runner, prometheus)
  2. Or uncomment the registry bucket and S3 secret to restore the service

@clofour clofour merged commit 3e9c925 into main Apr 19, 2026
2 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant