Remote Development

A Deep Dive Into Space Dev Environments

Read this post in other languages:

A deep dive into Space dev environments

In JetBrains Space, you can launch dev environments to work on your code remotely. A powerful, dedicated virtual machine will run a Docker container that has access to your project’s source code and provides backend components to your local code editor. You can use these remote machines to develop your software on, instead of using your local machine.

One of the many advantages of using Space dev environments is that you can standardize the dev environment for your team, so everyone can start working on a project almost instantly instead of wasting hours setting up their local machines. Additionally, you can run warm-up tasks and prepare a snapshot that has all the necessary package dependencies already downloaded and a pre-built project index that’s ready to go.

In this post, we’ll take a deep dive into these customization options of Space dev environments and see how you can tailor the dev environment to your team’s needs.

Creating a dev environment

Before we dive into the customization options, let’s quickly recap how to start developing with Space dev environments.

In any Git repository in your Space organization, you can launch a dev environment by clicking Open in IDE. Additionally, you can create a new dev environment for any merge request, so you don’t have to manually clone code and download dependencies on your local machine to review and test changes.

Create remote dev environment in JetBrains Space

Tip: You can migrate or mirror an existing Git repository from GitHub or BitBucket and make it available in Space, including all branches, tags, and commits. This way, you can make use of Space dev environments with an existing repository.

Depending on how much horsepower you need to work on your project, you can choose from one of three virtual machine instance types: Regular (4 CPU cores, 8 GB RAM), Large (8 CPU cores, 16 GB RAM), or Extra Large (16 CPU cores, 32 GB RAM).

Next, you’ll need to select the IDE you want to work with: IntelliJ IDEA with JetBrains Gateway (which can be downloaded via the Toolbox App), or JetBrains Fleet. Support for other IntelliJ-based IDEs is on the way.

Update: more IDEs are now supported in Space dev environments. You can now use IntelliJ IDEA, Fleet, GoLand, PhpStorm, PyCharm, RubyMine, WebStorm, and CLion (starting with the 2021.3 version)

You’ll also notice two other elements in this dialog: the warm-up snapshot and the dev container that is being used to start your remote IDE. In the example above, no warm-up snapshot is present yet (we’ll get to that in a moment), and the default Docker image is used as the container in which your IDE will be started.

When you click Create, Space will launch your IDE in the cloud. When the virtual machine is set up and the IDE backend is ready to go, JetBrains Gateway (or Fleet) will open and connect to it.

Running dev environment in IntelliJ IDEA (and JetBrains Gateway)

The IntelliJ IDEA backend is now running in the Space cloud, and we’re connected to it using a thin client: JetBrains Gateway.

For many projects, this is enough to start coding immediately. However, in this specific example, it looks like some things are missing:

  • We still have to download the correct JDK, as indicated by the warning in the editor.
  • We still need to run mvnw compile to download dependencies, and we need to synchronize the project in the IDE.

It’s probably fine to do this for a one-off dev environment, but if your entire team is going to be working on this repository, it would be better to customize the dev environment and make sure all dependencies are present from the start. Let’s see how to do this!

Customize the dev environment Dockerfile

By default, Space runs your dev environment using the default container image based on the Ubuntu OS, and includes Git, cURL, Docker, Docker Compose, and OpenJDK.

You can add a custom Dockerfile to your project to install any necessary tools and libraries. Depending on the IDE you will be using, you’ll have to create either ./.jb-gateway/Dockerfile (IntelliJ IDEA with JetBrains Gateway) or ./.fleet/Dockerfile (Fleet).

For this specific project, we’ll be customizing the environment to be based on Ubuntu 20.04, and we’ll also install a bunch of command line tools, including Git, cURL, Docker, and more. We’ll also add several versions of OpenJDK and set the default to version 16 so the dev environment can pick it up.

FROM ubuntu:20.04

ENV DEBIAN_FRONTEND=noninteractive
ENV LC_ALL=C.UTF-8

RUN apt-get update && apt-get install -y apt-utils apt-transport-https

RUN apt-get install -y \
  # Utilities \
  curl unzip wget software-properties-common socat man-db gnupg2 pass lsof \
  # VCS \
  git \
  # JVM \
  openjdk-8-jre-headless openjdk-11-jdk-headless openjdk-16-jdk-headless openjdk-17-jdk-headless maven \
  # Docker
  docker docker-compose \
  && rm -rf /var/lib/apt/lists/*


ENV JAVA_HOME=/usr/lib/jvm/java-16-openjdk-amd64

There are a couple of requirements for a custom Dockerfile:

  • The OS has to be a glibc-based Linux distribution (for example, CentOS 7+, Debian 9+, or Ubuntu 20.04+).
  • You have to install Git, OpenSSH (if you want to use a remote Git repository), and lsof (if you need port forwarding in the IDE).
  • The container must run as root (without any non-root users in the Dockerfile).

Note that the Dockerfile is branch specific. This makes it easy to test customizations in a separate branch without affecting other developers on your team, to update to newer tooling versions in a feature branch, and so on.

When you commit and push this customized Dockerfile to your project’s repository, Space will use it as the base image when creating a new dev environment in this branch.

Custom Dockerfile / Docker container for a Space remote dev environment

After starting this customized dev environment, you’ll see that the correct JDK version is now available to the IDE. However, the IntelliJ IDEA and Maven projects still have to be synced. This is the perfect job for a warm-up script!

Warming up the dev environment

You can shorten the time it takes for the IDE to resolve project dependencies, build indexes, and perform other background activities by building a warm-up snapshot. In our example, running mvnw compile and indexing the project would help us get the dev environment ready to go.

Warm-up snapshots are created with Space Automation. By adding a .space.kts file, you can not only start configuring continuous integration (CI) for your project, but also configure how your dev environment should be warmed up.

Here’s an example .space.kts file that defines one job that runs every night, downloads all Git branches, and then runs the steps to prepare a warm-up snapshot with IntelliJ IDEA:

job("Dev Environment Warmup - Gateway") {
    startOn {
        schedule { cron("0 5 * * *") }
    }

    git {
        depth = UNLIMITED_DEPTH
        refSpec = "refs/*:refs/*"
    }

    warmup(ide = Ide.IJGateway) {
        scriptLocation = "warmup.sh"
    }
}

The scriptLocation is optional. When it’s omitted, Space Automation will clone your project’s Git repository and handle project indexing in the IDE. When it’s added, you can specify the name of a warm-up script, like warmup.sh, and in that script do things like run mvnw compile to download all Maven dependencies into your warm-up snapshot, or maybe run an npm install. Here’s an example warmup.sh script:

#!/bin/bash
./mvnw compile

Note that the warmup.sh must be committed to your repository with executable permissions. You can do this by running git update-index --chmod=+x warmup.sh.

Once Space Automation finishes running the warm-up job, newly created dev environments will make use of the custom Dockerfile we created earlier and mount the warm-up snapshot we just created, with the project indexes and dependencies ready to go.

Dev environment with warm-up snapshot

If you ever need to troubleshoot a warm-up job, you can use Space Automation to find a log of all the steps that were executed along with their console output. 

Logs of a dev environment in Space

Additionally, from a project’s Dev Environments menu, you can manage and remove snapshots that are no longer needed.

Container image, warm-up snapshot, or both?

So far, we have seen how to customize the dev environment’s container image and how to build a warm-up snapshot. But where should you run which type of task?

As a rule of thumb, a custom container image should be used to configure the environment and operating system, while warm-up jobs should be used for project-specific tasks, such as downloading binary dependencies and preparing the IDE for your project.

Conclusion

In this post, we have looked at how to launch and customize dev environments in JetBrains Space. Using custom container images, you can standardize the dev environment for your team. Warm-up tasks let you prepare snapshots with downloaded package dependencies and pre-built project indexes, so you can start developing with the dev environment more quickly.

Give JetBrains Space and dev environments a try! We’d love to hear what you think!

image description