Introducing the new Render CLI and refreshed Render Dashboard.

Learn more
How-to
February 23, 2024

Using SSH keys for Private Package Management

W Ian Douglas
Render makes it easy to install your app's dependencies the same way you do in your local environment: using npm install, pip install, or whatever your package manager happens to be. If any of those dependencies are private packages, Render needs a little help accessing them, usually in the form of an SSH key. This blog post will show you how to set this up for deploying to Render.
Different stacks use different names for their installed dependencies (packages, libraries, modules, and so on). For simplicity, this post always uses "packages".
As a quick summary: we're going to walk you through the steps to encode your SSH key into a variable within an environment group so multiple projects can share the key, and write a custom build script to utilize that SSH key for your package management software.

Other prerequisites and assumptions

For the sake of brevity, we will skip instructions for logging into your Render dashboard. We will also assume that your "AcmeInc" team has a private GitHub repository with your package called "widgets", and that you have assigned your new SSH key (created below) to access the repository.

SSH keys versus tokens

Some package managers, such as Python's pip package manager, allow the use of environment variables to retrieve access tokens, but since this is not universally supported, we're going to focus on SSH keys. Many package managers enable you to access a Git repository by including an authentication token in the repo's URL. For example, with Python's pip package management, you might include this line in your requirements.txt file:
-e git+https://{$GITHUB_TOKEN_ENVVAR}@github.com/AcmeInc/widgets.git@{version}
The optional @{version} is replaced with a tag, branch, or commit hash (such as @deploy-branch). The {$GITHUB_TOKEN_ENVVAR} would be replaced from an environment variable of the same name. But as stated above, not all package managers allow for this kind of syntax.

Creating an SSH key

If you do not already have an SSH key, here are a few steps to generate a new key. SSH works in a key "pair": one private key, and one public key. We will be saving the new private key on Render. This command should work on most Mac/Linux systems to generate a strong SSH key pair:
$ ssh-keygen -t ed25519 -C "devteam@example.com"
This command will prompt you for a path and filename to create the key pair, and prompt you for a passphrase. We recommend NOT using a passphrase for this key pair, otherwise the deployment process will fail prompting for a password for each use from your package manager.

Environment groups

To begin, we're going to create an environment group, allowing you to share these environment settings across multiple projects and deployments within your team. Note that if you don't want to use an environment group, you can instead add the SSH_KEY environment variable we create below to each individual service that needs it.
Screenshot showing how to access the Environment Groups from the Render Dashboard
Screenshot showing how to access the Environment Groups from the Render Dashboard
From the Env Groups page in the Render Dashboard, click New Environment Group and give the group a name. In this case we're going to call the group "private details" that we'll use later.
Screenshot showing how an environment group can be given a custom name
Screenshot showing how an environment group can be given a custom name
Here we'll add a new environment variable named SSH_KEY. We'll first need to Base64-encode your SSH private key to paste in the correct value to be used later. Note that you'll provide the private key in your SSH key pair (it will be named id_ed25519 if you used the ssh-keygen command above), not the public key file ending in .pub.
$ base64 -i id_ed25519
LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS... (truncated for brevity)
This generates a Base64-encoded string that you can copy to your clipboard and paste as the value for your SSH_KEY environment variable:
Screenshot showing the SSH_KEY environment variable and its value pasted from our command-line work and Base64-encoded version of the SSH private key
Screenshot showing the SSH_KEY environment variable and its value pasted from our command-line work and Base64-encoded version of the SSH private key

Linking an environment group to your services

Each service with private dependencies requires access to the environment group where you made your SSH_KEY environment variable. We suggest using a blueprint where you can specify which keys you want to use from a given environment group, in this case the "private details" group that we created earlier.
services:
  - type: web
    name: my-project
    runtime: python
    repo: https://github.com/AcmeInc/our-project.git
    envVars:
      - fromGroup: private details
You can also add an environment group manually by navigating to your application on the dashboard and clicking on Environment, and finding the "Linked Environment Group" option at the bottom where you can add your newly created environment group:
Screenshot showing the navigation to an application's environment settings on Render to link your new environment group
Screenshot showing the navigation to an application's environment settings on Render to link your new environment group

An example build script

Finally, we want to create a customized build script for fetching your private packages. Let's look at an example script named build.sh that uses Python and pip (modify the last line of the script to use your own package manager):
#!/usr/bin/env bash

# Exit on error
set -o errexit

# Build a local .ssh folder on Render
mkdir -p ~/.ssh

# Add GitHub as a known host so it does not prompt you to add
# GitHub's IP address to known_hosts
ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2> /dev/null

# Copy your secret file to the local .ssh folder and chmod it.
# This grabs the SSH_KEY from your environment group and
# reverses the Base64 encoding to transform it back into a file
echo "$SSH_KEY" | base64 -d > ~/.ssh/id_ecdsa
chmod 400 ~/.ssh/id_ecdsa

# This next piece sets up your SSH configuration for all
# hosts. More info about these commands is available on the web.
cat > ~/.ssh/config << EOF
Host *
   StrictHostKeyChecking no
   UserKnownHostsFile /dev/null
   LogLevel ERROR
EOF

# Finally, we run our "pip" command.
# Replace this with "npm install" or any other command you
# use to install your dependencies
pip install -r requirements.txt
Make the build script executable with chmod +x build.sh and add it to your repository. Then, configure it as your service's buildCommand in your blueprint, or from your service's settings in the Render Dashboard under Settings > Build & Deploy.

Acknowledgments

The team at Render would like to acknowledge and thank Mitchell van der Hoeff from Readwise for their time and help to refine this process. We value input and insight from our users as we continue to build the best cloud deployment for you.