Skip Navigation
Show nav
Heroku Dev Center Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
Heroku Dev Center Dev Center
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log in or Sign up
View categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
    • Buildpacks
  • Developer Tools
    • AI Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Node.js Behavior in Heroku
      • Working with Node.js
      • Troubleshooting Node.js Apps
    • Ruby
      • Rails Support
        • Working with Rails
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • Working with PHP
      • PHP Behavior in Heroku
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
      • Heroku Postgres Advanced (Limited GA)
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Inference Essentials
    • Inference API
    • Inference Quick Start Guides
    • AI Models
    • Tool Use
    • AI Integrations
    • Vector Database
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
    • Heroku AppLink
      • Working with Heroku AppLink
      • Heroku AppLink Reference
      • Getting Started with Heroku AppLink
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
    • Other Salesforce Integrations
  • Databases & Data Management
  • Heroku Postgres
  • Connecting to a Private or Shield Heroku Postgres Database from an External Resource

Connecting to a Private or Shield Heroku Postgres Database from an External Resource

English — 日本語に切り替える

Table of Contents [expand]

  • Overview
  • Heroku Prerequisites
  • Configuring Mutual TLS and Allowlisting Your IP
  • Downloading Client-Side Certificates
  • Connecting to Your Database From an External Resource
  • Client Certificate Renewal
  • Configuring External Applications to Connect via mTLS
  • Revoking IP Rules
  • Disabling Mutual TLS
  • Configuring Mutual TLS via the Dashboard
  • mTLS CLI Plugin
  • Limitations and Caveats

Last updated May 18, 2026

Use Mutual TLS (mTLS) to create a secure and mutually authenticated channel between an external resource and a Heroku Postgres database running in a Private Space or a Shield Private Space. External resources can include any mTLS-enabled application or system running in private data centers or public clouds. To use this feature, you must also allowlist the external IP that connects to your database.

Mutual TLS isn’t supported on Heroku Postgres Advanced (Limited GA). Subscribe to our changelog to stay informed of when this feature is available for Advanced databases.

Overview

This feature is only available for Private and Shield Postgres databases.

Heroku provisions certificate authorities (CAs) in the Private Space and Shield Private Space, and generates certificates for the Postgres database server and your client. Your Postgres client can then configure the server certificate chain, client certificate chain, and client private key. You can provision more client certificates if needed.

This process involves three high-level steps:

  • Configuring Mutual TLS and allowlisting your external IP range
  • Downloading client-side certificates
  • Connecting to your database from an external resource

Shield Postgres Access

Heroku Prerequisites

Set up the following Heroku resources required for Mutual TLS:

  • A Private Space or Shield Private Space
  • A Heroku app running in the space with an attached Private or Shield Heroku Postgres database

Configuring Mutual TLS and Allowlisting Your IP

Step 1: Install the Mutual TLS CLI plugin

$ heroku plugins:install mtls

Step 2: Enable Mutual TLS

Enable Mutual TLS on your database with heroku data:mtls:create:

$ heroku data:mtls:create postgresql-rectangle-12345 --app example-app

Enabling Mutual TLS will allow external access to this restricted data service. This action will be logged. Continue?: yes
addon:           postgresql-rectangle-12345
app:             example-app
enabled_by:      example@heroku.com
status:          Provisioning
Enabling MTLS on postgresql-rectangle-12345... done
  • Replace postgresql-rectangle-12345 with the name of your Postgres database.
  • Replace example-app with the name of your app.

Track the enablement progress with heroku data:mtls DATABASE_NAME --app APP_NAME. The command shows the status of the mTLS feature for the database and its corresponding certificates:

$ heroku data:mtls postgresql-rectangle-12345 -a example-app
TLS-status: Provisioning
addon:      postgresql-rectangle-12345
app:        example-app

Certificates

 ID                                   Created At                Status
 ──────────────────────────────────── ───────────────────────── ───────
 4bb38419-a368-475f-ac9a-cf2b1b53b31a 2026-05-05 12:37:01 +0000 pending

It typically takes 5–10 minutes to enable Mutual TLS. When enabled, the TLS status for your database becomes Operational.

Step 3: Allowlist External IPs

Create one or more IP rules to allow external traffic from those IP blocks or ranges to reach your database. You can allowlist a hard limit of 60 IP blocks for each Heroku Postgres database. Run heroku data:mtls:ip-rules:create to create an IP rule:

$ heroku data:mtls:ip-rules:create postgresql-rectangle-12345 --app example-app \
  --cidr "1.2.3.4/32" \
  --description "My Office IP"

Creating IP Rule for database postgresql-rectangle-12345... done
cidr:        1.2.3.4/32
created_at:  2026-05-05 12:43:33 +0000
description: My Office IP
id:          38c466b6-dcfb-4869-b5ac-40420b786fb4
status:      Authorizing
updated_at:  2026-05-05 12:43:33 +0000
  • Replace postgresql-rectangle-12345 with the name of your Postgres database.
  • Replace example-app with your the name of your app.
  • Replace 1.2.3.4/32 with the IP range you want to allowlist, in Classless Inter-Domain Routing (CIDR) notation. An individual IP address is represented as x.x.x.x/32 where x can be any number between 0 and 255 (inclusive).
  • Replace My Office IP with a readable description of this allowlisted CIDR block.

Track the progress of the IP rule creation with heroku data:mtls:ip-rules:

$ heroku data:mtls:ip-rules postgresql-rectangle-12345 -a example-app
 ID                                   CIDR       Description  Created At                Status
 ──────────────────────────────────── ────────── ──────────── ───────────────────────── ───────────
 38c466b6-dcfb-4869-b5ac-40420b786fb4 1.2.3.4/32 My Office IP 2026-05-05 12:46:09 +0000 Authorizing

Alternatively, run heroku data:mtls:ip-rules:get to obtain the details of a specific IP rule, providing the IP rule ID with the --id option:

$ heroku data:mtls:ip-rules:get postgresql-rectangle-12345 --id "38c466b6-dcfb-4869-b5ac-40420b786fb4" --app example-app

cidr:        1.2.3.4/32
created_at:  2026-05-05 12:43:33 +0000
description: My Office IP
id:          38c466b6-dcfb-4869-b5ac-40420b786fb4
status:      Authorized
updated_at:  2026-05-05 12:44:33 +0000

It usually takes a few minutes to create an IP rule.

 

You can list all the IP addresses allowlisted for your Heroku Postgres database with heroku data:mtls:ip-rules DATABASE_NAME --app APP_NAME.

Downloading Client-Side Certificates

View all client-side certificates for your Heroku Postgres database with heroku data:mtls:certificates:

$ heroku data:mtls:certificates postgresql-rectangle-12345 --app example-app
 ID                                   Created At                Status
 ──────────────────────────────────── ───────────────────────── ──────
61acf66f-e505-452c-a517-e727689fb54f 2026-05-05 12:37:01 +0000 ready
  • Replace postgresql-rectangle-12345 with the name of your Postgres database.
  • Replace example-app with the name of your app.

Download the certificates needed to configure your client to access your Heroku Postgres database with heroku data:mtls:certificates:download:

$ heroku data:mtls:certificates:download postgresql-rectangle-12345 \
--id "61acf66f-e505-452c-a517-e727689fb54f" \
--prefix "demo_" \
--dir "./folder" \
--app example-app

Downloading certificates for database postgresql-rectangle-12345... done
your certs are now located in ./folder
  • Replace postgresql-rectangle-12345 with the name of your Postgres database.
  • Replace example-app with the name of your app.
  • Replace "61acf66f-e505-452c-a517-e727689fb54f" with the id of the certificate to download.
  • Replace "demo_" with a prefix to put in front of the names of the downloaded files.
  • Replace "./folder" with the directory to download files to. This value defaults to $HOME/.postgresql.

This command downloads the following files into the provided directory:

  • Client certificate with chain: PREFIXpostgresql.crt (in our example, demo_postgresql.crt)
  • Client private key: PREFIXpostgresql.key (in our example, demo_postgresql.key)
  • Server certificate chain: PREFIXroot.crt (in our example, demo_root.crt)

Set appropriate permissions for your private key. For the key downloaded in our example, set permissions with:

$ chmod 0600 ./folder/demo_postgresql.key

Postgres automatically detects and uses any postgresql.crt, postgresql.key and root.crt files in the user’s home directory. We recommend providing a prefix when downloading your certificates to help identify those certificates and avoid conflicts when connecting to other databases. Downloading your database’s certificates with their default filenames causes your local Postgres client to use them by default and it can interfere with other Heroku CLI operations or any other attempts to connect to a different database.

Connecting to Your Database From an External Resource

With mTLS enabled, your external apps or clients can access your Heroku Postgres database when they connect from an allowlisted IP address. These connections must provide a valid client certificate and use sslmode=verify-ca.

The specifics of your connection configuration vary depending on the database client you use. Use your database’s connection string and set the following connection parameters to configure your Postgres client to connect to your database using mTLS:

  • sslcert: path to the downloaded client certificate (DIRECTORY/PREFIXpostgresql.crt)
  • sslkey: path to the downloaded client private key (DIRECTORY/PREFIXpostgresql.key)
  • sslrootcert: path to the downloaded server certificate chain (DIRECTORY/PREFIXroot.crt)
  • sslmode: verify-ca

For example, to connect to a Heroku Postgres database with psql:

$ psql "${DATABASE_URL}?sslmode=verify-ca&sslcert=DIRECTORY/PREFIXpostgresql.crt&sslkey=DIRECTORY/PREFIXpostgresql.key&sslrootcert=DIRECTORY/PREFIXroot.crt"

psql (17.2 (Postgres.app), server 16.13 (Ubuntu 16.13-1.pgdg20.04+heroku2))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: none)
Type "help" for help.

DATABASE=>

Alternatively, instead of specifying these SSL parameters for every connection, you can set the related environment variables on your system:

$ export DATABASE_URL=`heroku config:get DATABASE_CONFIG_VAR -a APP_NAME`
$ export PGSSLCERT=DIRECTORY/PREFIXpostgresql.crt
$ export PGSSLKEY=DIRECTORY/PREFIXpostgresql.key
$ export PGSSLROOTCERT=DIRECTORY/PREFIXroot.crt
  • Replace DATABASE_CONFIG_VAR with the config var containing the URL of your Heroku Postgres database (for example, DATABASE_URL, HEROKU_POSTGRESQL_CRIMSON_URL).
  • Replace APP_NAME with your app’s name.
  • Replace DIRECTORY with the directory you downloaded the files to in the previous step.
  • Replace PREFIX with the prefix you put in front of the names of the downloaded files in the previous step.

It’s not necessary to set the PGSSLCERT, PGSSLKEY, and PGSSLROOTCERT config variables if you downloaded your files to the default directory without setting any prefix.

Client Certificate Renewal

A client certificate has an expiration date of 13 months after its creation date. 75 days before the expiration date, the following occurs:

  1. Heroku creates a new client certificate for you.
  2. Heroku sends an email to you about the pending expiration of your current certificate. The contents of the email includes details about your new certificate and how to begin using it.

After creating a new certificate, it’s recommended you begin using it before the current certificate expires and no longer works.

Configuring External Applications to Connect via mTLS

When configuring other apps to connect to your database via mTLS, we recommend referring to the documentation for that app or client to adapt to its specific requirements.

For example, apps that use the JDBC PostgreSQL connector require that the database connection string contains the sslcert, sslkey, sslroot and sslmode parameters, and a private key in either the PKCS-12 or PKCS-8 DER format.

The private key provided by the data:mtls:certificates:download command is in a PEM format. You can convert this key to DER format with openssl:

$ openssl pkcs8 -topk8 -inform PEM -in postgresql.key -outform DER -out postgresql.pk8 -nocrypt

Then, set appropriate permissions on the new key file:

$ chmod 0600 postgresql.pk8

See the official PostgreSQL documentation for more details on constructing JDBC connection URLs.

Revoking IP Rules

Delete an existing IP Rule from the Mutual TLS allowlist for your database with the heroku data:mtls:ip-rules:delete command. Specify the IP rule to delete with --id or --cidr options:

$ heroku data:mtls:ip-rules:delete postgresql-rectangle-12345 --id 38c466b6-dcfb-4869-b5ac-40420b786fb4 -a example-app
Deleting IP Rule for postgresql-rectangle-12345... done

$ heroku data:mtls:ip-rules:delete postgresql-rectangle-12345 --cidr 1.2.3.4/32 -a example-app
Deleting IP Rule for postgresql-rectangle-12345... done

Disabling Mutual TLS

Disable Mutual TLS with the heroku data:mtls:destroy command:

$ heroku data:mtls:destroy postgresql-rectangle-12345 -a example-app
Disabling MTLS on postgresql-rectangle-12345... done

Configuring Mutual TLS via the Dashboard

Alternatively, you can also enable mTLS and adjust allowed IPs for a Private or Shield Postgres database from the Heroku Dashboard. From the dashboard, select a Heroku Postgres database and then go to the Settings tab. To configure mTLS, click Show Configuration next to Secure External Database Access.

MTLS Dashboard

mTLS CLI Plugin

See the full documentation of the mTLS CLI plugin.

Limitations and Caveats

  • mTLS isn’t compatible with connection pooling.
  • When used with Trusted IPs for Data Services, mTLS IP rules and requirements take precedence over any overlapping rules specified in the Trusted IPs allowlist. Connections received from IP addresses allowlisted in both mTLS and Trusted IPs must provide a valid client certificate.

Feedback

Log in to submit feedback.

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2026 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices