Tips & Tricks

Using Docker with CLion

One of the common questions we get is why there is no Docker integration in CLion? In fact CLion works very nicely with Docker! There might not (yet) be a specific, “use Docker”, button – but setting it up yourself is actually quite easy and straightforward – even if you’re a Docker newbie!

Update: Staring with CLion 2021.3, you can configure special Docker toolchain in CLion. In this case, CLion will mount the project folder to it to avoid redundant source synchronization.

Read on if you prefer written instructions, or watch this video with the same content:

  1. Why use Docker?
  2. Getting Docker
  3. Creating a Docker container for use with CLion.
  4. Using the Remote Development workflow with Docker
  5. Changing the Docker environment
  6. Good luck getting Dockerized!

Why use Docker?

But why would you want to do this at all? Docker is not just the latest trend, but it represents a new way of setting up different environments in a quick, easy, reproducible and reliable way – that is easy to share, say, in version control.

Common uses for C++ developers are: working with multiple toolchains (especially different versions of the same compiler – which can be very tricky to maintain otherwise), working with cross-compilers for different platforms, or even just using a single environment but making it easy for everyone on your team to get the same environment quickly and easily (and keep it updated).

Docker is lighter-weight than a VM, but gives you essentially the same levels of isolation. It sounds complex to set-up, but one of the surprises for many is just how easy it is to get going.

Getting Docker

If you don’t already have it you’ll need to install the Docker Engine. This is fairly straightforward, although there may be a few extra steps, depending on your platform. The docs are excellent throughout, and the installation instructions are no exception – so check them out for your platform.

Creating a Docker container for use with CLion.

Docker containers are described in Dockerfiles. These look like simple scripts, but have a few special properties that make them a bit special.

First, you can refer to a “base image”, which is a prebuilt container, usually available via a docker registry, which does a lot of the heavy lifting. We’ll use a Ubuntu base image to give us a common platform to start with – but other OS images are available. E.g.

FROM ubuntu:18.04

Secondly, each line in a Dockerfile is executed on what Docker calls a layer. Each layer is immutable, once created – so any mutations on the layer above can be easily, and cheaply, rolled back – without having to recreate all the layers beneath.

That’s one of the things that makes Docker such a fast and productive environment once you get used to it. We won’t go into this in any more depth, here. It’s worth going through the introductory docs if you want to get more of a feel for it.

We’ve created a reference Dockerfile for you to get started. It should be fairly obvious how to change this to meet your needs.

Mostly you’ll change the apt-get part, which should be immediately familiar if you’ve used Ubuntu, before. Of course if you change the base image you may need to use a different approach to getting dependencies there. The point is it is just using the base image OS’s package manager (within Docker’s RUN command).

You may also be able to get (or create yourself) a base image with all the dependencies already set up.

RUN apt-get update \
  && apt-get install -y ssh \
    build-essential \
    gcc \
    g++ \
    gdb \
    clang \
    cmake \
    rsync \
    tar \
    python \
  && apt-get clean

The Dockerfile then sets up SSH, so CLion can connect into it, and creates a user for CLion to use.

RUN useradd -m user && yes password | passwd user

Usually the credentials needn’t be too obscure, but of course you can change them if you need to – or use another way (beyond the scope of this intro) to specify the details without mentioning them in the Dockerfile.

At the top of the Dockerfile, in the comments, are the three commands you’ll need to execute to build and run this Docker container.

The first builds the container:

docker build -t clion/remote-cpp-env:0.5 -f Dockerfile.remote-cpp-env .

This will take a few minutes to run, as it downloads the Ubuntu image (whether or not you are already running on Ubuntu), “installing” that into the first layer, installing all the dependencies (that apt-get line), setting up SSH and creating the user! Given all that it is surprisingly fast (and you should only have to do this once).

Once built you can run the container, using the next line:

docker run -d --cap-add sys_ptrace -p127.0.0.1:2222:22 --name clion_remote_env clion/remote-cpp-env:0.5

The -d runs the container as a daemon, so control returns back to you.
--cap-add sys_ptrace adds the ptrace capability, which is necessary for debugging. Docker containers are usually very minimal, by default, so you sometimes need to enable fundamental things.

-p2222:22 specifies a port mapping. Port 22 (the default SSH port) inside the container is mapped to outside the container as port 2222 on the host environment.

There’s nothing special about 2222. Doubling up, or multiplying by 10 are common ways of expressing mapped ports, but you could specify any available port number, here. That can be useful if you intend to run multiple containers side-by-side (e.g. to support alternate toolchains).

Whatever port number you chose, since it’s only temporarily mapped, and may be reused, it’s usually worth clearing any cached SSH keys:

ssh-keygen -f "$HOME/.ssh/known_hosts" -R "[localhost]:2222"

Don’t worry if that tells you it couldn’t find any – likely the first time.

So now the Docker container is up and running, ready with your toolchain installed. All that’s left is to get CLion to use it.

Using the Remote Development workflow with Docker

Since we set-up SSH in our container we can connect into it using CLion’s standard Remote Development features. See our help pages for full details on setting up Full Remote Mode.

In short, you should add a new Toolchain entry in settings under Build, Execution, Deployment as a Remote Host type.

Then click in the Credentials section and fill out the SSH credentials we set-up in the Dockerfile (Host: localhost, Port: 2222, User name: user, Password: password).

Finally, add a CMake profile that uses this toolchain and you’re done. If you want more fine grained control over the paths used inside the container take a look at the Deployment settings page.

Either way, once you commit (Apply and/ or OK) out of settings, and wait for the CMake profile to load, and files to transfer, you can then select the CMake profile from the Run Config drop-down, and your code will now be compiled, run and debugged, within the container using the specified toolchain.

If the container has an earlier version of CMake than your local project (and you created the project, first, locally) then you may need to set the CMake version back at the top of the CMakeLists.txt file.

You could also get a later version of CMake in the Dockerfile but that’s beyond the scope of this post.

Changing the Docker environment

If you need to make changes to the container you may need to re-run the build step, as well. Consult the Docker docs to be sure, as it’s not always obvious whether a change has taken effect, due to Docker’s extensive caching (usually a good thing).

If you do rebuild you’ll probably also have to reload the CMake project, as well.

Good luck getting Dockerized!

That should be all you need to get started. Be sure to read the Docker docs to get the most out of Docker, itself. And check out the Remote Development docs in the CLion help for a few more details that may be helpful, too.

image description