Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Guides for CARTO Self-hosted using Kubernetes and Helm



appConfigValues:
bigqueryOauth2ClientId: "<value_from_credentials_web_client_id>"
appSecrets:
bigqueryOauth2ClientSecret:
value: "<value_from_credentials_web_client_secret>"In this post you'll find all you need to do to enable the AI Features in Self-Hosted using Helm.
For CARTO Self-hosted using Kubernetes and Helm



appSecrets:
googleMapsApiKey:
value: "<REDACTED>"kubectl create secret generic \
[-n my-namespace] \
mycarto-google-maps-api-key \
--from-literal=googleMapsApiKey=<REDACTED>appSecrets:
googleMapsApiKey:
existingSecret:
name: mycarto-google-maps-api-key
key: googleMapsApiKeyhelm get values -n <namespace> <release-name> -o yaml | yq '.internalPostgresql.enabled'CREATE DATABASE aiproxy;kubectl cluster-info kubectl config current-contextkubectl get namespaceshelm list -Ahelm get values -n <namespace> <release-name> -o yaml | yq '.externalPostgresql.user'GRANT ALL PRIVILEGES ON DATABASE aiproxy TO <carto_user>;
ALTER DATABASE aiproxy OWNER TO <carto_user>;appConfigValues:
## Enable CARTO AI Pre-requisites
aiFeaturesEnabled: trueexternalPostgresql:
## [... existing config...]
## aiproxy database for AI functionalties
aiProxyDatabaseName: "<aiproxy-custom-database-name>"cartoConfigValues:
cartoDataWarehouseEnabled: trueFor CARTO Self-hosted using Kubernetes and Helm



For CARTO Self-hosted using Kubernetes and Helm
For CARTO Self-hosted using Kubernetes and Helm
For CARTO Self-hosted using Kubernetes and Helm
appConfigValues:
publicEventsApiEnabled: trueCREATE USER {USERNAME};
GRANT rds_iam TO {USERNAME};{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "rds-db:connect",
"Resource": "arn:aws:rds-db:{REGION}:{AWS_ACCOUNT_ID}:dbuser:{RDS_DATABASE_RESOURCE_ID}/{USERNAME}"
}
]
}commonBackendServiceAccount:
create: false
name: '{EKS_POD_IDENTITY_SERVICE_ACCOUNT}'
externalPostgresql:
host: {HOST}
user: {USERNAME}
database: {DATABASE}
port: {PORT}
awsEksPodIdentityEnabled: true
awsRdsRegion: {REGION}
sslEnabled: true
sslCA: |
{CA_CERT}commonBackendServiceAccount:
create: false
name: '{EKS_POD_IDENTITY_SERVICE_ACCOUNT}'
appConfigValues:
awsEksPodIdentityBucketsEnabled: trueappConfigValues:
ssoOrganizationId: "<YOUR_ORGANIZATION_ID>"{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "<your_aws_user_arn>"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}appConfigValues:
importAwsRoleArn: "<your_aws_user_arn>"
appSecrets:
importAwsAccessKeyId:
value: "<your_aws_user_access_key_id>"
importAwsSecretAccessKey:
value: "<your_aws_user_access_key_secret>"{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<your_aws_s3_bucket_name>"
},
{
"Effect": "Allow",
"Action": "s3:*Object",
"Resource": "arn:aws:s3:::<your_aws_s3_bucket_name>/*"
}
]
}For CARTO Self-hosted using Kubernetes and Helm
For CARTO Self-hosted using Kubernetes and Helm
externalProxy.sslCA (optional): Path to the proxy CA certificate. If the proxy certificate is signed by a custom CA, such CA must be included here, but if it's signed by a well known CA, there is no need to add it here. Well known CAs are usually part of the ca-certificates package.snowflakecomputing.com to the list of excluded domains.
externalProxy:
enabled: true
host: <Proxy IP/Hostname>
port: <Proxy port>
type: http
excludedDomains: ["localhost,.svc.cluster.local"]externalProxy:
enabled: true
host: <Proxy IP/Hostname>
port: <Proxy port>
type: https
excludedDomains: ["localhost,.svc.cluster.local"]
## NOTE: Please, carefully read CARTO Self-hosted proxy documentation to understand the the current limitations with [custom CAs].
sslRejectUnauthorized: true
# sslCA: |
# -----BEGIN CERTIFICATE-----
# XXXXXXXXXXXXXXXXXXXXXXXXXXX
# -----END CERTIFICATE-----apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: carto-no-internet-access
# Optional labels
labels:
app.kubernetes.io/instance: <instance-name>
app.kubernetes.io/managed-by: <managed-by>
app.kubernetes.io/name: <app-name>
spec:
# Match all Pods except the proxy Pod.
# NOTE: Make sure your proxy pod has the label app.kubernetes.io/component: proxy
# NOTE: Proxy pod is not deployed with the CARTO Self-hosted chart
podSelector:
matchExpressions:
- key: app.kubernetes.io/component
operator: NotIn
values:
- proxy
- router
policyTypes:
- Ingress
- Egress
ingress:
# Allow connections within the same namespace
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: <namespace>
egress:
# Allow connections within the same namespace
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: <namespace>
# Allow DNS resolution
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
# Allow connections to external datawarehouses
- to:
- ipBlock:
cidr: 0.0.0.0/0
ports:
# Postgres
- port: 5432
protocol: TCP
# Redshift
- port: 5439
protocol: TCP
# Valkey
- port: 6379
protocol: TCP
# Allow connections to other datawarehouses hosts on port 443
- to:
- ipBlock:
cidr: <CIDR>
ports:
- port: 443
protocol: TCPappConfigValues:
defaultAtLocation:
bigquery: "carto-un.carto"
snowflake: "CARTO.CARTO"
redshift: "carto"
databricks: "carto"
postgres: "carto"router:
extraEnvVars:
- name: IFRAME_ALLOWED_DOMAINS
value: "https://mydomain1.com https://mydomain2.com:8080"appConfigValues:
enableTrackJS: falserouter:
extraEnvVars:
- name: NGINX_CLIENT_MAX_BODY_SIZE
value: "10M"
- name: NGINX_GZIP_MIN_LENGTH
value: "1100"
- name: NGINX_GZIP_BUFFERS
value: "16 8k"
- name: NGINX_PROXY_BUFFERS
value: "16 8k"
- name: NGINX_PROXY_BUFFER_SIZE
value: "8k"
- name: NGINX_PROXY_BUSY_BUFFERS_SIZE
value: "8k"max_connections = pool_size * number_connections * number_nodes;MAPS_API_V3_DYNAMIC_TILES_QUERY_TIMEOUT_POSTGRES_MS=20000
MAPS_API_V3_DYNAMIC_TILES_QUERY_TIMEOUT_REDSHIFT_MS=20000
MAPS_API_V3_DYNAMIC_TILES_QUERY_TIMEOUT_SNOWFLAKE_MS=20000
MAPS_API_V3_DYNAMIC_TILES_QUERY_TIMEOUT_BIGQUERY_MS=20000
MAPS_API_V3_DYNAMIC_TILES_QUERY_TIMEOUT_DATABRICKS_MS=20000kubectl get secret <postgresql_secret_name> \
-n <namespace> \
-ogo-template='{{ index .data "postgres-password" }}' | base64 -Dkubectl get pods -n <namespace> | grep postgresqlkubectl exec <postgresql_pod_name> \
-n <namespace> \
-- pg_dump --dbname=postgresql://postgres:<postgres_password>@localhost:5432/workspace > dump.sqlCREATE USER workspace_admin with encrypted password '<password>';CREATE DATABASE workspace OWNER workspace_admin;psql -h <external_db_host> -p <external_db_port> -U workspace_admin -d workspace -f dump.sqlkubectl create secret generic \
-n
internalRedis:
# Disable the internal Valkey
enabled: false
externalRedis:
...
tlsEnabled: trueexternalRedis:
...
tlsEnabled: true
tlsCA: |
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----internalRedis:
# Disable the internal Valkey
enabled: false
externalRedis:
host: <Valkey IP/Hostname>
port: "6379"
password: <Valkey password>
tlsEnabled: true
# Only applies if your Valkey TLS certificate it's self-signed
# tlsCA: |
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----WORKLOAD_IDENTITY_SA_EMAIL: Service account email configured for Workload Identity.curl -s 'https://accounts.app.carto.com/users/me' \
-H 'Authorization: Bearer <your_carto_jwt_token>' \
| jq '.user_id'gcloud iam service-accounts create {IAM_SERVICE_ACCOUNT_NAME} \
--project={PROJECT_ID}gcloud iam service-accounts add-iam-policy-binding \
{IAM_SERVICE_ACCOUNT_EMAIL} \
--member=serviceAccount:{IAM_SERVICE_ACCOUNT_EMAIL} \
--role=roles/iam.serviceAccountTokenCreatorcommonBackendServiceAccount:
enableGCPWorkloadIdentity: true
annotations:
iam.gke.io/gcp-service-account: "{IAM_SERVICE_ACCOUNT_EMAIL}"gcloud iam service-accounts add-iam-policy-binding {IAM_SERVICE_ACCOUNT_EMAIL} \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:{PROJECT_ID}.svc.id.goog[{KUBERNETES_NAMESPACE}/{KUBERNETES_SERVICE_ACCOUNT}]"workspaceApi:
extraEnvVars:
- name: WORKSPACE_SYNC_DATA_ENABLED
value: "true"
- name: WORKSPACE_WORKLOAD_IDENTITY_WORKFLOWS_TEMP
value: {WORKFLOWS_TEMP_LOCATION}
- name: WORKSPACE_WORKLOAD_IDENTITY_BILLING_PROJECT
value: {BILLING_PROJECT_ID}
- name: WORKSPACE_WORKLOAD_IDENTITY_SERVICE_ACCOUNT_EMAIL
value: {WORKLOAD_IDENTITY_SA_EMAIL}
- name: WORKSPACE_WORKLOAD_IDENTITY_CONNECTION_OWNER_ID
value: {CARTO_OWNER_ID}
workspaceSubscriber:
extraEnvVars:
- name: WORKSPACE_SYNC_DATA_ENABLED
value: "true"
- name: WORKSPACE_WORKLOAD_IDENTITY_WORKFLOWS_TEMP
value: {WORKFLOWS_TEMP_LOCATION}
- name: WORKSPACE_WORKLOAD_IDENTITY_BILLING_PROJECT
value: {BILLING_PROJECT_ID}
- name: WORKSPACE_WORKLOAD_IDENTITY_SERVICE_ACCOUNT_EMAIL
value: {WORKLOAD_IDENTITY_SA_EMAIL}
- name: WORKSPACE_WORKLOAD_IDENTITY_CONNECTION_OWNER_ID
value: {CARTO_OWNER_ID}gcloud iam service-accounts add-iam-policy-binding \
{WORKLOAD_IDENTITY_SA_EMAIL} \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:{PROJECT_ID}.svc.id.goog[{KUBERNETES_NAMESPACE}/carto-common-backend]" \
--project {PROJECT_ID}router:
service:
type: LoadBalancerrouter:
service:
type: LoadBalancer
annotations:
# Use AWS Network Load Balancer (NLB) or Classic (CLB)
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
# Reference an ACM Certificate ARN for SSL termination
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-east-1:123456789012:certificate/uuid"
# Configure SSL ports
service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
# Connection settings
service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "605"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"router:
service:
annotations:
cloud.google.com/load-balancer-type: "Internal"router:
service:
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"router:
service:
type: ClusterIP # Default value# Example: Configuration for TLS Offloading
## Disable HTTPS and disable cert autogeneration.
tlsCerts:
httpsEnabled: false
autoGenerate: false
router:
service:
type: ClusterIP
ports:
http: 80# 1. Encode the full certificate chain (including intermediates)
cat /path/to/fullchain.pem | base64 | tr -d '\n' > cert.b64
# 2. Encode the private key
cat /path/to/privkey.pem | base64 | tr -d '\n' > key.b64
# 3. View the encoded values (you'll copy these into your values file)
echo "Certificate (base64):"
cat cert.b64
echo ""
echo "Private Key (base64):"
cat key.b64# Example: Configuration for End-to-End TLS
## Enable HTTPS and disable cert autogeneration.
tlsCerts:
httpsEnabled: true
autoGenerate: false
router:
service:
type: ClusterIP
ports:
https: 443
httpsTargetPort: "https"
## Add the base64 cert keys to the router.
tlsCertificates:
certificateValueBase64: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZaRENDQk..."
privateKeyValueBase64: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQU..."


appSecrets:
googleCloudStorageServiceAccountKey:
value: |
<REDACTED>appConfigValues:
storageProvider: "azure-blob"
azureStorageAccount: <storage_account_name>
workspaceImportsBucket: <import_bucket_name>
workspaceImportsPublic: <false|true>
workspaceThumbnailsBucket: <thumbnails_bucket_name>
thumbnailsBucketExternalURL: <external bucket URL>
workspaceThumbnailsPublic: <false|true>WORKSPACE_THUMBNAILS_PUBLIC="false"appConfigValues:
storageProvider: "gcp"
workspaceImportsBucket: <import_bucket_name>
workspaceImportsPublic: <false|true>
workspaceThumbnailsBucket: <thumbnails_bucket_name>
workspaceThumbnailsPublic: <false|true>
thumbnailsBucketExternalURL: <public or authenticated external bucket URL>
googleCloudStorageProjectId: <gcp_project_id>appConfigValues:
workspaceExportsBucket: <YOUR_EXPORTS_BUCKET>{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "<your_aws_user_arn>"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}appConfigValues:
awsExportBucket: <BUCKET_NAME>
awsExportBucketRegion: <REGION>
exportAwsRoleArn: <ROLE_ARN>appSecrets:
exportAwsSecretAccessKey:
value: <REDACTED>
exportAwsAccessKeyId:
value: <REDACTED>kubectl create secret generic \
[-n my-namespace] \
mycarto-export-aws-access-key \
--from-literal=key-id=<ACCESS_KEY_ID>
--from-literal=key-secret=<ACCESS_KEY_SECRET>appSecrets:
exportAwsSecretAccessKey:
existingSecret:
name: mycarto-export-aws-access-key
key: key-id
exportAwsAccessKeyId:
existingSecret:
name: mycarto-export-aws-access-key-id
key: key-secret{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<your_aws_s3_bucket_name>"
},
{
"Effect": "Allow",
"Action": "s3:*Object",
"Resource": "arn:aws:s3:::<your_aws_s3_bucket_name>/*"
}
]
}kubectl create secret generic \
[-n my-namespace] \
mycarto-custom-s3-secret \
--from-literal=awsAccessKeyId=<REDACTED> \
--from-literal=awsSecretAccessKey=<REDACTED>appSecrets:
awsAccessKeyId:
existingSecret:
name: mycarto-custom-s3-secret
key: awsAccessKeyId
awsAccessKeySecret:
existingSecret:
name: mycarto-custom-s3-secret
key: awsSecretAccessKeykubectl create secret generic \
[-n my-namespace] \
mycarto-custom-azure-secret \
--from-literal=azureStorageAccessKey=<REDACTED>appSecrets:
awsAccessKeyId:
existingSecret:
name: mycarto-custom-azure-secret
key: azureStorageAccessKeykubectl create secret generic \
[-n my-namespace] \
mycarto-google-storage-service-account \
--from-file=key=<PATH_TO_YOUR_SECRET.json>appSecrets:
googleCloudStorageServiceAccountKey:
existingSecret:
name: mycarto-google-storage-service-account
key: keyappConfigValues:
storageProvider: "s3"
workspaceImportsBucket: <import_bucket_name>
workspaceImportsPublic: <false|true>
workspaceThumbnailsBucket: <thumbnails_bucket_name>
workspaceThumbnailsPublic: <false|true>
thumbnailsBucketExternalURL: <external bucket URL>
awsS3Region: <s3_buckets_region>appSecrets:
awsAccessKeyId:
value: "<REDACTED>"
awsAccessKeySecret:
value: "<REDACTED>"appSecrets:
azureStorageAccessKey:
value: "<REDACTED>"