←  back to guides
Connect Multiple Repositories on Gitpod using Tailscale

Connect Multiple Repositories on Gitpod using Tailscale

So, you are working on multiple services that need to talk to each other. Each part of your application lives in a different repository, or you might be collaborating on an integration project involving more than one product from different teams. With Gitpod, you set up secure and isolated cloud workspaces for your git repositories.

But how do we make workspaces talk to each other securely?

We have teamed up with our friends at Tailscale to bring an easy way to solve this problem using their simple zero-config VPN, which comes pre-installed in Gitpod workspaces. Tailscale’s secure mesh technology based on WireGuard can connect machines securely across the internet, such as your Gitpod workspaces or a cloud or on-prem resource, like a database, frictionlessly. ✨

This guide will show how easy it is to connect Gitpod workspaces over a secure tunnel provided by Tailscale. Let’s get started 🚀

A case for working with multiple repositories at a time

A typical architecture these days is server-side rendering (SSR). You generate the view by calling backend services on the frontend server instead of calling your API from client devices. If you develop on your local machine, two repositories will be open in two IDE windows. Every service can communicate with each other as they are all running on the same machine.

Multiple services can communincate with each-other when running on the same machine
Local setup

In Gitpod, each workspace runs in a secure sandbox. You can expose ports so that only your browser can access them. However, the workspaces cannot communicate with one another.

In Gitpod each workspace runs in a secure sandbox, workspaces cannont communicate with one another
Gitpod workspaces are secure and isolated

As long as only the browser interacts with the API, this will work with Gitpod without any changes. If you develop single-page applications and static sites where every network request is a client-side fetch, you don’t need Tailscale.

But if your application has a server-side that needs to fetch data from another workspace, it becomes a networking problem. The application server requires a secure network tunnel between them to send a request outside of its workspace and into the API workspace.

This is what Tailscale is made for ✨

Connect multiple workspaces on Gitpod using Tailscale
Establish secure tunnel using Tailscale

Connecting multiple workspaces together with Tailscale

We can run Tailscale in each of our Gitpod workspaces, which will make them part of your “tailnet”, a secure VPN consisting of your machines that can access each other. Traffic over your “tailnet” is fully end-to-end encrypted, with each workspace having its private key, so anyone, not even Tailscale, is capable of reading the traffic.

In Gitpod, each workspace can log into Tailscale and receive a list of secure IP addresses of other workspaces connected to your “tailnet”. It is only possible to connect to these IPs from machines running Tailscale, which can be your workspaces or your local machines.

Tailscale comes pre-installed with gitpod/workspace-full, Gitpod’s base image for workspaces. So if you are using the default image or a custom docker image based on workspace-full, you are ready to go 🚀; else, you will need to add instructions to install Tailscale into your workspace.

1. Add Tailscale to your .gitpod.yml tasks

Scroll to the bottom of this page for an example .gitpod.yml file, or follow these steps to set it up for yourself.

The “Connect to Tailscale” task will prompt you to log in. We only need to do this once. Next time, we fetch the token from Gitpod’s environment variables to skip the login.

The “Restore Tailscale daemon” task launches Tailscale and puts it in the background. It connects the workspace to your “tailnet” using your previously saved Tailscale token.

language icon yml
tasks:
    - name: Restore Tailscale daemon
      command: |
          if [ -n "${TS_STATE_TAILSCALE_EXAMPLE}" ]; then
            # restore the tailscale state from gitpod user's env vars
            sudo mkdir -p /var/lib/tailscale
            echo "${TS_STATE_TAILSCALE_EXAMPLE}" | sudo tee /var/lib/tailscale/tailscaled.state > /dev/null
          fi
          sudo tailscaled
    - name: Connect to Tailscale
      command: |
          if [ -n "${TS_STATE_TAILSCALE_EXAMPLE}" ]; then
            sudo -E tailscale up
          else
            sudo -E tailscale up --hostname "gitpod-${GITPOD_GIT_USER_NAME// /-}-$(echo ${GITPOD_WORKSPACE_CONTEXT} | jq -r .repository.name)"
            # store the tailscale state into gitpod user
            gp env TS_STATE_TAILSCALE_EXAMPLE="$(sudo cat /var/lib/tailscale/tailscaled.state)"
          fi
          exit

2. Open a workspace for each repository

Commit the .gitpod.yml to your repository.

Next time when you launch your workspace, your Gitpod terminal will give you a login link with a unique token. Once you’ve logged in, it will connect your workspace to your Tailscale account.

Tailscale allows you to log in via GitHub, Google, Microsoft, or email. As long as you are logged in to the same organization in each of your repositories, your Gitpod workspaces will be able to send requests to each other.

3. View your connected workspaces

Run tailscale status to see the private IP addresses for your other workspaces. These are only accessible to other Tailscale nodes. Your workspaces can communicate with each other, but neither your browser nor anyone else will be able to access them.

$ tailscale status
100.11.166.123  main-backend-service username@  linux   -
100.11.201.28   main-application username@  linux   -

Gitpod is all about giving you a great developer experience. So if you have to look up IP addresses every time you make a workspace, it isn’t great.

We can use a .gitpod.yml task to search Tailscale and create environment variables for each connected service.

Let’s say backend-service is the name of the repository you want to connect. You can find its IP address by running the following command:

$ tailscale status | grep backend-service | cut -d " " -f 1
100.11.166.123

You can set the result as an environment variable so that your application can use it later.

In this example, this task looks for backend-service and sets an environment variable named API_URL pointing to it before launching the application.

language icon yml
- name: Start application
  init: npm install
  command: |
      REPO_NAME=backend-service
      API_IP=$(tailscale status | grep $REPO_NAME | cut -d " " -f 1)
      if [ "${API_IP}" ]; then
        echo "🐳 Connected to $REPO_NAME through Tailscale"
        API_URL="http://$API_IP:5000/api" npm run dev
      else
        echo "🐳 Failed to connect to $REPO_NAME. Make sure a $REPO_NAME workspace is active and logged into Tailscale."
        npm run dev
      fi
  env:
      PORT: 3000
      NODE_ENV: development

To try this out, add this task to the tasks list in your .gitpod.yml file, commit it, and try it out with a new workspace. Your workspaces should be able to send requests to each other through their secure Tailscale IP addresses. Now you are fully set up for multi-repo development on Gitpod. ✨

Sample .gitpod.yml

language icon yml
image: gitpod:workspace/full
ports:
    - port: 3000
      onOpen: ignore
tasks:
    - name: Restore Tailscale daemon
      command: |
          if [ -n "${TS_STATE_TAILSCALE_EXAMPLE}" ]; then
            # restore the tailscale state from gitpod user's env vars
            sudo mkdir -p /var/lib/tailscale
            echo "${TS_STATE_TAILSCALE_EXAMPLE}" | sudo tee /var/lib/tailscale/tailscaled.state > /dev/null
          fi
          sudo tailscaled
    - name: Start application
      init: |
          eval $(gp env -e)
          npm install
      command: |
          REPO_NAME=backend-service
          API_IP=$(tailscale status | grep $REPO_NAME | cut -d " " -f 1)
          if [ "${API_IP}" ]; then
            echo "🐳 Connected to $REPO_NAME through Tailscale"
            API_URL="http://$API_IP:5000/api" npm run dev
          else
            echo "🐳 Failed to connect to $REPO_NAME. Make sure a $REPO_NAME workspace is active and logged into Tailscale."

            npm run dev
          fi
      env:
          PORT: 3000
          NODE_ENV: development
    - name: Connect to Tailscale
      command: |
          if [ -n "${TS_STATE_TAILSCALE_EXAMPLE}" ]; then
            sudo -E tailscale up
          else
            sudo -E tailscale up --hostname "gitpod-${GITPOD_GIT_USER_NAME// /-}-$(echo ${GITPOD_WORKSPACE_CONTEXT} | jq -r .repository.name)"
            # store the tailscale state into gitpod user
            gp env TS_STATE_TAILSCALE_EXAMPLE="$(sudo cat /var/lib/tailscale/tailscaled.state)"
          fi
          exit
Author
@jacobparis's avatar on GitHub Jacob Paris
@nancy-chauhan's avatar on GitHub Nancy Chauhan

Publish date

May 6, 2022

Join developers, everywhere.

Development environments pre-configured with the tools and dependencies needed to get inspired and start building.

Related articles

Monthly Newsletter

Subscribe to get a summary of what we've shipped over the last month and everything you need to know around developer experience.

By submitting this form, I confirm that I acknowledge the collection and processing of personal data by Gitpod, as further described in the Privacy Policy.