Orchestrated container deployment

This documentation is for the CARTO Self-Hosted Legacy Version. Use only if you've installed this specific version. Explore our latest documentation for updated features.

Estimated time: Completing this deployment guide is expected to take approximately 3 hours. This estimate may vary based on individual familiarity with the technology stack involved and the complexity of your organization's environment.


To deploy CARTO Self-Hosted on Kubernetes, you need:

  • A CARTO Self-Hosted installation package containing your environment configuration and a license key. The package has two YAML files: carto-secrets.yaml and carto-values.yaml If you don't have it yet, you can ask for it at support@carto.com.

  • A domain you own, to which you can add a DNS record.

  • A Kubernetes cluster. To create a cluster, see documentation on Google Cloud Platform, AWS, and Azure. This cluster must fit our hardware and software requirements for Kubernetes.

  • A working installation of kubectl. To install kubectl, see documentation on Google Cloud Platform, AWS, and Azure.

  • A working installation of Helm v3 on version 3.6.0 or later.

1. Add Helm repository

Type this command to add the helm repository

# Add the carto repo.
helm repo add carto https://helm.carto.com

Search for the repo to confirm you can access the carto chart.

helm search repo carto -l

2. Add your minimal customization


Create a file customizations.yaml with the domain name you want to use

  selfHostedDomain: "my.domain.com"

A full domain is required. You cannot install CARTO in a domain path like https://my.domain.com/carto

External database

At this point, we are setting up the configuration of the external database. You need to provide a PostgreSQL admin user (typically postgres) with permission to create users and databases.

The installation requires an admin user (postgres). This admin user will create a user and database.

Create the secret with both passwords:

kubectl create secret generic \
  carto-postgresql \
  --from-literal=admin-password='<adminPassword>' \
  --from-literal=user-password='<userPassword>' \

Edit customizations.yaml:

  • adminUser: Your PostgreSQL admin user.

  • admin-password: The password of your admin user.

  • user: The carto user to be created. It will be created with the previous admin user.

  • user-password: The new password to be created.

  • database: The database to be created.

  # Disable the internal Postgres
  enabled: false
  host: <YourServerIPorDNS>
  user: "carto_worskpace_admin"
  adminUser: "postgres"
  existingSecret: "carto-postgresql"
  existingSecretPasswordKey: "user-password"
  existingSecretAdminPasswordKey: "admin-password"
  database: "carto_workspace"
  port: "5432"


In some scenarios, an SSL connection to the external database is required. In that case, you should add to customizations.yaml:

  sslEnabled: true
  # Only applies if your PostgreSQL SSL certificate it's self-signed
    sslCA: |
    -----END CERTIFICATE-----

Mutual TLS connections between the external database and the APIs are not supported, so client certificates can't be configured on your external database

3. Install CARTO

Use the following command to install CARTO in your Kubernetes cluster.

helm install \
  carto \
  carto/carto \
  -f carto-values.yaml \
  -f carto-secrets.yaml \
  -f customizations.yaml

After installing CARTO, verify you have the required pods running by running the following command (It can take up to 5 minutes to have everything running):

kubectl get pods
NAME                                          READY   STATUS    RESTARTS   AGE
carto-accounts-www-5778cc7c7f-l42rx           1/1     Running   0          3m6s
carto-cdn-invalidator-sub-bd69b7b96-c6mmt     1/1     Running   0          3m5s
carto-http-cache-864d54db46-56xs2             1/1     Running   0          3m6s
carto-import-api-c845759bd-9m7bd              0/1     Running   0          3m7s
carto-import-worker-5bf456f684-b9mg9          1/1     Running   0          3m7s
carto-lds-api-676dcdd9c8-lqmcf                1/1     Running   0          3m6s
carto-maps-api-5699c99fb6-9dj5s               1/1     Running   0          3m4s
carto-maps-api-5699c99fb6-msfr2               1/1     Running   0          3m4s
carto-notifier-cf8f7576d-5wnm5                1/1     Running   0          3m5s
carto-redis-master-0                          1/1     Running   0          3m6s
carto-router-7578cdd848-mbrxw                 2/2     Running   0          3m5s
carto-sql-worker-64d7598c56-zt5xr             1/1     Running   0          3m6s
carto-workspace-api-66dbbdb69-cqd4p           1/1     Running   0          3m5s
carto-workspace-api-66dbbdb69-z4tj9           0/1     Running   0          3m4s
carto-workspace-subscriber-54f9cbc589-qk94m   1/1     Running   0          3m5s
carto-workspace-www-7bb5f78577-k66rr          1/1     Running   0          3m6s

Once all the services are running, verify the installation by port forwarding to localhost. Execute the following:

kubectl port-forward --namespace default svc/carto-router 80:80 443:443

Add to your /etc/hosts the domain you will use:

echo " my.domain.com" >> /etc/hosts

Configure an HTTPS endpoint

The entry point to the CARTO Self-Hosted is through the router service. By default, it is configured in ClusterIP mode, and it's only accessible in your machine with kubectl port-forward).

The first step here is to make the router accessible using a LoadBalancer. This provides an externally-accessible IP address that we will later configure our DNS.

Add to customizations.yaml based on the cloud you are using:

    type: LoadBalancer

After that, upgrade the cluster:

helm upgrade carto \
  carto/carto \
  -f carto-values.yaml \
  -f carto-secrets.yaml \
  -f customizations.yaml

Get the EXTERNAL-IP of your CARTO Self-Hosted deployment with this command:

$ kubectl get svc --namespace default -w carto-router

NAME           TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)                      AGE
carto-router   LoadBalancer   80:32339/TCP,443:32602/TCP   8h

At this point, you need to configure a DNS record that points my.domain.com to the EXTERNAL-IP (

Use your own TLS certificate

By default, the package generates a self-signed certificate with a validity of 365 days.

If you want to add your own certificate you need to create a secret:

kubectl create secret tls -n <namespace> <certificate name> \
  --cert=path/to/cert/file \

Edit customizations.yaml:

  httpsEnabled: true
  autoGenerate: false
    name: "<certificate name>"
    keyKey: "tls.key"
    certKey: "tls.crt"

Apply the changes:

helm upgrade carto \
  carto/carto \
  -f carto-values.yaml \
  -f carto-secrets.yaml \
  -f customizations.yaml

If your TLS certificate key is protected with a passphrase the CARTO Self-hosted installation won't be able to work as expected. You can easily generate a new key file without passphrase protection using the following command:

openssl rsa -in keyfile_with_passphrase.key -out new_keyfile.key

Configure your storage buckets

CARTO Self-hosted platform needs access to some storage buckets to save some resources needed by the platform. These buckets are in charge of storing assets such as imported datasets, map snapshots and custom markers.

You can create and use your own storage buckets in any of the following supported storage providers:

And in order to configure them, there is a detailed guide available that you should follow to complete the Self-Hosted configuration process.

Post-installation checks

In order to verify CARTO Self Hosted was correctly installed, and it's functional, we recommend performing the following checks:

  1. Sign in to your Self Hosted, create a user and a new organization.

  2. Go to the Connections page, in the left-hand menu, create a new connection to one of the available providers.

  3. Go to the Data Explorer page, click on the Upload button right next to the Connections panel. Import a dataset from a local file.

  4. Go back to the Maps page, and create a new map.

  5. In this new map, add a new layer from a table using the connection created in step 3.

  6. Create a new layer from a SQL Query to the same table. You can use a simple query like:

SELECT * FROM <dataset_name.table_name> LIMIT 100;
  1. Create a new layer from the dataset imported in step 4.

  2. Make the map public, copy the sharing URL and open it in a new incognito window.

  3. Go back to the Maps page, and verify your map appears there, and the map thumbnail represents the latest changes you made to the map.

Congrats! Once you've configured your custom buckets, you should have a production-ready deployment of CARTO Self-Hosted at https://my.domain.com

You may notice that the onboarding experience (demo maps, demo workflows...) and the Data Observatory-automated features (subscriptions, enrichment...) are disabled by default in your new organization, because the CARTO Data Warehouse is not enabled.

If you'd like to enable the onboarding experience and the Data Observatory features, follow the guide to enable the CARTO Data Warehouse or contact support@carto.com.

If you prefer not to enable the CARTO Data Warehouse, you can still use the Data Observatory without the UI features: after getting in touch, our team can deliver the data (both premium and public subscriptions) manually to your data warehouse.

Analytics Toolbox in CARTO Self-Hosted

To fully leverage CARTO's capabilities you need to gain access to the Analytics Toolbox functions. This step is crucial to fully leverage CARTO's capabilities. Please refer to the documentation of your data warehouse provider for detailed instructions:


The following standard commands of kubectl could be used to debug possible issues that might arise:

kubectl logs and kubectl describe


The container workspace-migrations (included at the pod carto-workspace-api-*) will be responsible for creating a new user carto_worskpace_admin and a database carto_workspace.

To debug possible errors with the connection of the external database you might need to check the logs of this container:

kubectl logs carto-workspace-api-585f4bbd7c-qt65g -c workspace-migrations

For further assistance check our Support page.

Last updated