<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://ohcnwiki.tellmey.fyi/index.php?action=history&amp;feed=atom&amp;title=Deployment%2FProviders%2FGcp%2FDeploying_Care_with_the_GCP_template</id>
	<title>Deployment/Providers/Gcp/Deploying Care with the GCP template - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://ohcnwiki.tellmey.fyi/index.php?action=history&amp;feed=atom&amp;title=Deployment%2FProviders%2FGcp%2FDeploying_Care_with_the_GCP_template"/>
	<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=Deployment/Providers/Gcp/Deploying_Care_with_the_GCP_template&amp;action=history"/>
	<updated>2026-09-04T18:15:50Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.4</generator>
	<entry>
		<id>https://ohcnwiki.tellmey.fyi/index.php?title=Deployment/Providers/Gcp/Deploying_Care_with_the_GCP_template&amp;diff=1197&amp;oldid=prev</id>
		<title>Admin: Add GCP IaC (OpenTofu + Helm) deployment guide based on egovhealthcare/gcp_template (via create-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=Deployment/Providers/Gcp/Deploying_Care_with_the_GCP_template&amp;diff=1197&amp;oldid=prev"/>
		<updated>2026-09-02T08:17:52Z</updated>

		<summary type="html">&lt;p&gt;Add GCP IaC (OpenTofu + Helm) deployment guide based on egovhealthcare/gcp_template (via create-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Doc header&lt;br /&gt;
|type=guide&lt;br /&gt;
|domain=deployment&lt;br /&gt;
|title=Deploying Care with the GCP template&lt;br /&gt;
|order=2&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
This guide covers deploying Care on Google Cloud using the &amp;#039;&amp;#039;&amp;#039;[https://github.com/egovhealthcare/gcp_template gcp_template]&amp;#039;&amp;#039;&amp;#039; repository — an infrastructure-as-code template built with &amp;#039;&amp;#039;&amp;#039;OpenTofu&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Helm&amp;#039;&amp;#039;&amp;#039;. It provisions the entire platform (network, cluster, databases, storage, encryption keys) and then installs Care and its supporting services as Helm releases.&lt;br /&gt;
&lt;br /&gt;
This is the &amp;#039;&amp;#039;&amp;#039;recommended&amp;#039;&amp;#039;&amp;#039; path for new GCP deployments. It replaces the older click-through console workflow described in [[Deployment/Providers/Gcp/Deploying Care on GKE|Deploying Care on GKE]], which relied on manually created resources, Cloud Source Repositories, and the &amp;lt;code&amp;gt;infra_template&amp;lt;/code&amp;gt; manifests.&lt;br /&gt;
&lt;br /&gt;
{{Note|1=The repository is the source of truth. Variable names, defaults, and chart values change over time — always check the repository&amp;#039;s &amp;lt;code&amp;gt;README.md&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;environments/sample.tfvars&amp;lt;/code&amp;gt;, and root &amp;lt;code&amp;gt;variables.tf&amp;lt;/code&amp;gt; against the version you are deploying.}}&lt;br /&gt;
&lt;br /&gt;
== What the template provisions ==&lt;br /&gt;
&lt;br /&gt;
The template is split into four OpenTofu modules, each with its own state and its own &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Module&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pre-infra/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Project bootstrap — enables the required Google Cloud APIs and optionally creates the Cloud DNS zone.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;infra/&amp;lt;/code&amp;gt;&lt;br /&gt;
| The core platform — VPC and subnets, the GKE cluster and node pools, Cloud SQL for PostgreSQL, Cloud Storage buckets, Cloud Armor, Workload Identity Federation for GitHub, an optional jump host, and optional Scribe credentials.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;KMS/&amp;lt;/code&amp;gt;&lt;br /&gt;
| The KMS key ring and the customer-managed encryption keys used to encrypt Cloud Storage buckets and other resources.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;deploy/&amp;lt;/code&amp;gt;&lt;br /&gt;
| The Kubernetes layer — namespace, Secrets and ConfigMaps, and the Helm releases for every application.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Two further directories support these modules: &amp;lt;code&amp;gt;helm_charts/&amp;lt;/code&amp;gt; holds the in-repo Helm charts, and &amp;lt;code&amp;gt;environments/&amp;lt;/code&amp;gt; holds the sanitised tfvars template. Helper scripts for synchronising configuration with Secret Manager live in &amp;lt;code&amp;gt;scripts/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Applications installed by the deploy module ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Chart&lt;br /&gt;
! What it runs&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;care_be&amp;lt;/code&amp;gt;&lt;br /&gt;
| The Care backend — the API deployment, the Celery worker, the Celery beat scheduler, and a database migration job. Includes optional HPAs for the API and the worker.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;care_fe&amp;lt;/code&amp;gt;&lt;br /&gt;
| The Care frontend, served as a container behind the gateway.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;gateway&amp;lt;/code&amp;gt;&lt;br /&gt;
| The Gateway API resources, the cert-manager &amp;lt;code&amp;gt;ClusterIssuer&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Certificate&amp;lt;/code&amp;gt;, the HTTP-to-HTTPS redirect, and the GCP gateway policy.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;redis&amp;lt;/code&amp;gt;&lt;br /&gt;
| Redis, used as the Celery broker and the cache.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;metabase&amp;lt;/code&amp;gt;&lt;br /&gt;
| Metabase, backed by its own Cloud SQL instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dcm4chee&amp;lt;/code&amp;gt;&lt;br /&gt;
| Optional. The DICOM stack — dcm4chee-arc, OpenLDAP, an nginx proxy, and the OHIF viewer. Enabled with &amp;lt;code&amp;gt;enable_dicom&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;cert-manager&amp;#039;&amp;#039;&amp;#039; itself is installed from the upstream Jetstack chart with the Gateway API integration enabled, so certificates are issued directly against the Gateway.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;OpenTofu&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Helm&amp;#039;&amp;#039;&amp;#039; installed locally.&lt;br /&gt;
* The &amp;#039;&amp;#039;&amp;#039;Google Cloud SDK&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;gcloud&amp;lt;/code&amp;gt;), authenticated against the target project.&lt;br /&gt;
* A GCP project with billing enabled, and a project number.&lt;br /&gt;
* A &amp;#039;&amp;#039;&amp;#039;GCS bucket&amp;#039;&amp;#039;&amp;#039; to hold the OpenTofu remote state.&lt;br /&gt;
* Permission to read and write Secret Manager secrets in the project.&lt;br /&gt;
* Domain names for the frontend, the API, and (if used) Metabase and DICOM.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All configuration is supplied as a single tfvars file per environment. The canonical template is &amp;lt;code&amp;gt;environments/sample.tfvars&amp;lt;/code&amp;gt;; the source of truth for every variable is the root &amp;lt;code&amp;gt;variables.tf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Real environment files are &amp;#039;&amp;#039;&amp;#039;never committed&amp;#039;&amp;#039;&amp;#039;. They live in Secret Manager under the naming convention &amp;lt;code&amp;gt;tofu-tfvars-&amp;amp;lt;env&amp;amp;gt;&amp;lt;/code&amp;gt;, and each module&amp;#039;s &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt; pulls the file down at runtime before planning or applying.&lt;br /&gt;
&lt;br /&gt;
{{Note|1=Only sanitised samples belong in the repository. Treat &amp;lt;code&amp;gt;environments/&amp;amp;lt;env&amp;amp;gt;.tfvars&amp;lt;/code&amp;gt; on your workstation as a transient artefact.}}&lt;br /&gt;
&lt;br /&gt;
=== Required environment variables ===&lt;br /&gt;
&lt;br /&gt;
Set these before running any &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; target:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variable&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PROJECT_ID&amp;lt;/code&amp;gt;&lt;br /&gt;
| The GCP project ID. May also be supplied as &amp;lt;code&amp;gt;TF_VAR_project_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ENV_NAME&amp;lt;/code&amp;gt;&lt;br /&gt;
| The environment name. May also be supplied as &amp;lt;code&amp;gt;TF_VAR_environment&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;TF_VAR_env_name&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;BACKEND_BUCKET&amp;lt;/code&amp;gt;&lt;br /&gt;
| The GCS bucket holding the OpenTofu state.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;export BACKEND_BUCKET=&amp;quot;&amp;lt;your-state-bucket&amp;gt;&amp;quot;&lt;br /&gt;
export PROJECT_ID=&amp;quot;&amp;lt;your-gcp-project-id&amp;gt;&amp;quot;&lt;br /&gt;
export ENV_NAME=&amp;quot;&amp;lt;environment-name&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Preparing the tfvars file ===&lt;br /&gt;
&lt;br /&gt;
# Copy the template: &amp;lt;code&amp;gt;cp environments/sample.tfvars environments/&amp;amp;lt;env&amp;amp;gt;.tfvars&amp;lt;/code&amp;gt;&lt;br /&gt;
# Edit the values for your environment.&lt;br /&gt;
# Push it to Secret Manager so the modules can retrieve it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd pre-infra&lt;br /&gt;
make push-tfvars PROJECT_ID=&amp;lt;gcp-project&amp;gt; ENV_NAME=&amp;lt;env&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Override the default file path with &amp;lt;code&amp;gt;LOCAL_TFVARS_FILE=&amp;amp;lt;path&amp;amp;gt;&amp;lt;/code&amp;gt; if your file lives elsewhere.&lt;br /&gt;
&lt;br /&gt;
=== Key configuration groups ===&lt;br /&gt;
&lt;br /&gt;
The tfvars file is organised into the following groups. See &amp;lt;code&amp;gt;environments/sample.tfvars&amp;lt;/code&amp;gt; for the full annotated template.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Group&lt;br /&gt;
! Notable variables&lt;br /&gt;
|-&lt;br /&gt;
| Environment identity&lt;br /&gt;
| &amp;lt;code&amp;gt;region&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;project_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;project_number&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;zones&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;zone&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;org&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;app&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;environment&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Domains and DNS&lt;br /&gt;
| &amp;lt;code&amp;gt;web_domain_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;api_domain_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;metabase_domain_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;dicom_domain_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enable_dns_zone&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;dns_zone_domain&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Cluster&lt;br /&gt;
| &amp;lt;code&amp;gt;node_pools&amp;lt;/code&amp;gt; (machine type, counts, disk, node locations, private nodes), &amp;lt;code&amp;gt;cluster_name&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Networking&lt;br /&gt;
| &amp;lt;code&amp;gt;database_subnets&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;gke_subnets&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;gke_pods_range&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;gke_services_range&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;proxy_only_subnet_cidr&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Databases&lt;br /&gt;
| &amp;lt;code&amp;gt;cloudsql_tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cloudsql_disk_size&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cloudsql_read_replica_count&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cloudsql_read_replica_tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;metabase_cloudsql_tier&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Feature toggles&lt;br /&gt;
| &amp;lt;code&amp;gt;enable_dicom&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enable_cloud_armor&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enable_github_wif&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enable_scribe&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enable_jumphost&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enable_legacy_ingress&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enable_local_cors&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Applications&lt;br /&gt;
| &amp;lt;code&amp;gt;helm_config&amp;lt;/code&amp;gt; — image repository and tag per service, plus the API and Celery worker autoscaling settings&lt;br /&gt;
|-&lt;br /&gt;
| Secrets and config&lt;br /&gt;
| &amp;lt;code&amp;gt;jwks_base64&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;additional_secrets&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;additional_config_map_data&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| TLS&lt;br /&gt;
| &amp;lt;code&amp;gt;external_tls_cert&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;external_tls_key&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;external_tls_base_domains&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Tip|1=Domain variables are &amp;#039;&amp;#039;&amp;#039;lists&amp;#039;&amp;#039;&amp;#039;, even when you only have one hostname — &amp;lt;code&amp;gt;web_domain_name = [&amp;quot;app.example.org&amp;quot;]&amp;lt;/code&amp;gt;. Cloud SQL sizing values are numbers, not quoted strings.}}&lt;br /&gt;
&lt;br /&gt;
=== Node pools ===&lt;br /&gt;
&lt;br /&gt;
Node pools are configured as a list of objects. Each entry sets the machine type, the autoscaling bounds, the disk size, the zones the pool spans, and whether the nodes are private.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;hcl&amp;quot;&amp;gt;node_pools = [&lt;br /&gt;
  {&lt;br /&gt;
    name                 = &amp;quot;default&amp;quot;&lt;br /&gt;
    machine_type         = &amp;quot;e2-standard-2&amp;quot;&lt;br /&gt;
    min_count            = 1&lt;br /&gt;
    max_count            = 2&lt;br /&gt;
    preemptible          = false&lt;br /&gt;
    disk_size_gb         = 100&lt;br /&gt;
    node_locations       = &amp;quot;asia-south1-a,asia-south1-b&amp;quot;&lt;br /&gt;
    enable_private_nodes = true&lt;br /&gt;
  },&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Application images and autoscaling ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;helm_config&amp;lt;/code&amp;gt; selects the image for each service and controls the horizontal pod autoscalers on the Care backend. Autoscaling is CPU-based and scales on requests, so it only behaves sensibly when resource requests are set.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;hcl&amp;quot;&amp;gt;helm_config = {&lt;br /&gt;
  care_backend = {&lt;br /&gt;
    repository = &amp;quot;asia-south1-docker.pkg.dev/example-project/staging/care&amp;quot;&lt;br /&gt;
    tag        = &amp;quot;latest&amp;quot;&lt;br /&gt;
    # api_autoscaling_enabled      = true&lt;br /&gt;
    # api_autoscaling_min_replicas = 2&lt;br /&gt;
    # api_autoscaling_max_replicas = 6&lt;br /&gt;
    # api_autoscaling_target_cpu   = 80&lt;br /&gt;
  }&lt;br /&gt;
  care_frontend = {&lt;br /&gt;
    repository = &amp;quot;asia-south1-docker.pkg.dev/example-project/staging/care_fe&amp;quot;&lt;br /&gt;
    tag        = &amp;quot;latest&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
  metabase = {&lt;br /&gt;
    repository = &amp;quot;metabase/metabase&amp;quot;&lt;br /&gt;
    tag        = &amp;quot;v0.63.13&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
  redis = {&lt;br /&gt;
    repository = &amp;quot;redis&amp;quot;&lt;br /&gt;
    tag        = &amp;quot;8-alpine&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== TLS certificates ===&lt;br /&gt;
&lt;br /&gt;
By default, cert-manager issues certificates for every configured hostname through the Gateway. If you already hold a wildcard certificate, supply it with &amp;lt;code&amp;gt;external_tls_cert&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;external_tls_key&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;external_tls_base_domains&amp;lt;/code&amp;gt;. cert-manager continues to issue certificates for any hostname the wildcard does not cover.&lt;br /&gt;
&lt;br /&gt;
The deploy module also creates a self-signed placeholder certificate so the Gateway can come up before cert-manager has issued the real one.&lt;br /&gt;
&lt;br /&gt;
== Deploying ==&lt;br /&gt;
&lt;br /&gt;
Modules must be applied &amp;#039;&amp;#039;&amp;#039;in order&amp;#039;&amp;#039;&amp;#039;. The &amp;lt;code&amp;gt;deploy/&amp;lt;/code&amp;gt; module reads remote state outputs from both &amp;lt;code&amp;gt;infra/&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;KMS/&amp;lt;/code&amp;gt;, so those must exist first.&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;code&amp;gt;pre-infra/&amp;lt;/code&amp;gt;&lt;br /&gt;
# &amp;lt;code&amp;gt;infra/&amp;lt;/code&amp;gt;&lt;br /&gt;
# &amp;lt;code&amp;gt;KMS/&amp;lt;/code&amp;gt;&lt;br /&gt;
# &amp;lt;code&amp;gt;deploy/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|1=Confirm the module ordering against the &amp;lt;code&amp;gt;README.md&amp;lt;/code&amp;gt; in the revision you are deploying. The relationship between &amp;lt;code&amp;gt;infra/&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;KMS/&amp;lt;/code&amp;gt; has been revised, and the correct order depends on whether the infra module consumes KMS keys directly.}}&lt;br /&gt;
&lt;br /&gt;
=== Per-module commands ===&lt;br /&gt;
&lt;br /&gt;
Each module exposes the same targets. Run them from inside the module directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;make init BACKEND_BUCKET=&amp;lt;state-bucket&amp;gt;&lt;br /&gt;
make pull-tfvars PROJECT_ID=&amp;lt;gcp-project&amp;gt; ENV_NAME=&amp;lt;env&amp;gt;&lt;br /&gt;
make plan PROJECT_ID=&amp;lt;gcp-project&amp;gt; ENV_NAME=&amp;lt;env&amp;gt; BACKEND_BUCKET=&amp;lt;state-bucket&amp;gt;&lt;br /&gt;
make deploy PROJECT_ID=&amp;lt;gcp-project&amp;gt; ENV_NAME=&amp;lt;env&amp;gt; BACKEND_BUCKET=&amp;lt;state-bucket&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;make plan&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;make deploy&amp;lt;/code&amp;gt; both run &amp;lt;code&amp;gt;pull-tfvars&amp;lt;/code&amp;gt; first, so the plan always reflects what is currently in Secret Manager. &amp;lt;code&amp;gt;make deploy&amp;lt;/code&amp;gt; is interactive unless you pass &amp;lt;code&amp;gt;AUTO_APPROVE=true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Two further targets are available: &amp;lt;code&amp;gt;make lint&amp;lt;/code&amp;gt; formats the OpenTofu files recursively, and &amp;lt;code&amp;gt;make destroy&amp;lt;/code&amp;gt; tears the module down.&lt;br /&gt;
&lt;br /&gt;
=== A full first deployment ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;export BACKEND_BUCKET=&amp;quot;&amp;lt;state-bucket&amp;gt;&amp;quot;&lt;br /&gt;
export PROJECT_ID=&amp;quot;&amp;lt;gcp-project&amp;gt;&amp;quot;&lt;br /&gt;
export ENV_NAME=&amp;quot;&amp;lt;env&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
cd pre-infra&lt;br /&gt;
make init BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
make plan  PROJECT_ID=$PROJECT_ID ENV_NAME=$ENV_NAME BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
make deploy PROJECT_ID=$PROJECT_ID ENV_NAME=$ENV_NAME BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
&lt;br /&gt;
cd ../infra&lt;br /&gt;
make init BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
make plan  PROJECT_ID=$PROJECT_ID ENV_NAME=$ENV_NAME BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
make deploy PROJECT_ID=$PROJECT_ID ENV_NAME=$ENV_NAME BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
&lt;br /&gt;
cd ../KMS&lt;br /&gt;
make init BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
make plan  PROJECT_ID=$PROJECT_ID ENV_NAME=$ENV_NAME BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
make deploy PROJECT_ID=$PROJECT_ID ENV_NAME=$ENV_NAME BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
&lt;br /&gt;
cd ../deploy&lt;br /&gt;
make init BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
make plan  PROJECT_ID=$PROJECT_ID ENV_NAME=$ENV_NAME BACKEND_BUCKET=$BACKEND_BUCKET&lt;br /&gt;
make deploy PROJECT_ID=$PROJECT_ID ENV_NAME=$ENV_NAME BACKEND_BUCKET=$BACKEND_BUCKET&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== After the first apply ===&lt;br /&gt;
&lt;br /&gt;
# Read the Gateway&amp;#039;s external IP address from the cluster and create DNS &amp;#039;&amp;#039;&amp;#039;A&amp;#039;&amp;#039;&amp;#039; records for each hostname pointing at it.&lt;br /&gt;
# Wait for cert-manager to replace the placeholder certificate with an issued one.&lt;br /&gt;
# Confirm the migration job for the Care backend completed successfully before treating the deployment as live.&lt;br /&gt;
&lt;br /&gt;
== Releasing a new version ==&lt;br /&gt;
&lt;br /&gt;
Application releases are configuration changes, not infrastructure changes. To roll out a new Care build:&lt;br /&gt;
&lt;br /&gt;
# Update the &amp;lt;code&amp;gt;tag&amp;lt;/code&amp;gt; for the relevant service under &amp;lt;code&amp;gt;helm_config&amp;lt;/code&amp;gt; in your environment&amp;#039;s tfvars.&lt;br /&gt;
# Push the updated file to Secret Manager with &amp;lt;code&amp;gt;make push-tfvars&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;make plan&amp;lt;/code&amp;gt; and then &amp;lt;code&amp;gt;make deploy&amp;lt;/code&amp;gt; from the &amp;lt;code&amp;gt;deploy/&amp;lt;/code&amp;gt; module.&lt;br /&gt;
&lt;br /&gt;
Only the &amp;lt;code&amp;gt;deploy/&amp;lt;/code&amp;gt; module needs to run — the platform modules are untouched.&lt;br /&gt;
&lt;br /&gt;
The backend Helm chart annotates its pods with checksums of the rendered Secret and ConfigMap, so a change to configuration triggers a rollout automatically rather than leaving pods running with stale values.&lt;br /&gt;
&lt;br /&gt;
== Operational notes ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Never commit real tfvars.&amp;#039;&amp;#039;&amp;#039; Secret Manager is the only place environment configuration should live.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Plan before every apply.&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;make plan&amp;lt;/code&amp;gt; runs with &amp;lt;code&amp;gt;-lock=false&amp;lt;/code&amp;gt;, so it is safe to run alongside other readers, but it will not protect you from a concurrent apply.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Helm history is capped&amp;#039;&amp;#039;&amp;#039; at ten revisions per release, which bounds how far back &amp;lt;code&amp;gt;helm rollback&amp;lt;/code&amp;gt; can go.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Legacy naming overrides&amp;#039;&amp;#039;&amp;#039; exist for most resources (&amp;lt;code&amp;gt;cluster_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;vpc_network_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;namespace_name&amp;lt;/code&amp;gt;, and others) so an existing deployment can be adopted into the template without renaming live infrastructure. Leave them unset for a greenfield deployment and let the naming convention apply.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;The jump host&amp;#039;&amp;#039;&amp;#039; is optional and controlled by &amp;lt;code&amp;gt;enable_jumphost&amp;lt;/code&amp;gt;. It takes its SSH keys from &amp;lt;code&amp;gt;jumphost_ssh_keys&amp;lt;/code&amp;gt;. Disable it if you reach the cluster another way.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Deployment/Providers/Gcp|Google Cloud overview]] — choosing between the deployment options on GCP.&lt;br /&gt;
* [[Deployment/Providers/Gcp/Deploying Care on GKE|Deploying Care on GKE]] — the earlier console-driven workflow.&lt;br /&gt;
* [[Deployment/Kubernetes/Reference architecture|Kubernetes reference architecture]] — the provider-agnostic production setup.&lt;br /&gt;
* [[Deployment/Security/Data security|Data security]] — security practices that apply to every deployment.&lt;br /&gt;
&lt;br /&gt;
{{Navbox deployment}}&lt;br /&gt;
&lt;br /&gt;
{{Related}}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>