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.

Comments below can no longer be edited.

24 Responses to Using Docker with CLion

  1. Avatar

    Johnathan Raymond says:

    January 14, 2020

    Probably shouldn’t expose the ssh port you create to the world – not a big deal, but:

    docker run -d –cap-add sys_ptrace -p”” –name clion_remote_env clion/remote-cpp-env:0.5

    • Avatar

      Vasily Romanikhin says:

      January 14, 2020

      Thanks, we will update the post (and dockerfile header) properly.

    • Avatar

      Jan Hudec says:

      April 21, 2020

      At least on Linux it is not necessary to expose the port at all. Any port open in the container is accessible from localhost on the IP address assigned to the container. I am not sure about Windows where it actually runs in a virtual though and don’t have a Windows box handy to test now.

  2. Avatar

    Eric Pederson says:

    January 15, 2020

    Does this work like the WSL2 integration? It seems like you should be able to take advantage of bind mounts to avoid transferring files (especially on Windows CLion).

    • Avatar

      Phil Nash says:

      January 15, 2020

      Eric (and Alexey, who asks essentially the same question): we don’t, currently, support mounted volumes in this way – but appreciate that it would be useful. We are looking at adding it to an upcoming release, but I can’t give you specifics just yet.

      • Avatar

        Alexey says:

        January 15, 2020

        I’ve just changed the flavour of deployment from ‘SFTP’ to ‘local or mounted folder’. And then pointed to the parent folder relative to project root folder.

        In ‘mapping’ I’ve set local path (absolute path to project root) and ‘deployment path’ (just project root).

        In cmake I usually have build folder relative to parent of project root (i.e. something like ../project-build/Debug).

        With all this in mind I can run ‘reload’ and ‘rebuild’.
        Both works well, project can be built and I can run debug.
        In all these actions NO active file transfers noticed, i.e. clion works as expected and doesn’t upload/download anything.

        So, what do you men by ‘we don’t currently support it’? That it has to be set up manually?
        Well, that is not the big deal (especially taking in mind that it is usually onetime action, and you don’t need to run it again and again on every build).

        Then I’ve noticed that all now works without transferring

        • Avatar

          Phil Nash says:

          January 16, 2020

          Nice and creative 🙂
          Glad you got it to work – and anyone reading this can certainly try the above at their own risk – this is not “officially” supported, and has some issues. Apparently this will break after every CLion restart – and may be unpredictable in other ways.

          As I say, we aim to have a properly supported method in the near future, but probably not the next release.

        • Avatar

          Eric Pederson says:

          January 23, 2020

          Hi Alexey –

          Which host OS are you using? I tried this approach with a Windows 10 host and got close but not quite. CLion loads the CMakeLists.txt and generates the build system in the container, but none of the build targets show up in the Run Configuration menu.

      • Avatar

        Eric Pederson says:

        January 21, 2020

        Good to hear!

        Here’s a recent blog post from Docker that is related:

  3. Avatar

    Alexey says:

    January 15, 2020

    1. On installing `build-essential` you don’t need to install gcc and g++, they’re redundant.

    2. Dockerized build often used for build not only cross-platform, but kind of ancient systems where cmake may be either not shipped, either ancient. So, it is reasonable to directly install fresh one from tarball as separate layer. For example, by `ADD cmake-3.14.0-Linux-x86_64.tar.gz /` command. Then you need either create .bashrc in the docker and add ‘export PATH….’ with path to cmake, either just write this path when set up toolchain in clion – to find this fresh cmake.

    3. On another flavours (say, centos 6 or centos 7) you also need to run ssh-keygen at the end of ssh install chunk of the script. Otherwise docker will not have host key, and it is not immediately obvious how to fix it. Piece looks like `useradd -m user \
    && yes password | passwd user \
    && ssh-keygen -f /etc/ssh/ssh_host_ecdsa_key -N ” -t ecdsa -b 521 \
    && ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N ” -t rsa \
    && ssh-keygen -f /etc/ssh/ssh_host_dsa_key -N ” -t dsa`

    4. It is extremely useful to set also mounted volume when running docker (with -v option) to make your sources available directly from the host. And then when setting up toolchain – set deployment method to ‘local or mounted folder’. That will make debugging and developing much more transparent, and also save for you time and disk space.

    • Avatar

      Phil Nash says:

      January 15, 2020

      Thanks Alexey. See response to Eric regarding (4). For (2) and (3) that’s valuable additional information – thanks.
      For (1), that’s also useful to know. Maybe we’ll update the Dockerfiles. On the other hand it can be useful, for the sake of the reference example, to show the tool being explicitly mentioned. Either way, thanks for your input.

  4. Avatar

    Richard Szabo says:

    January 17, 2020

    Well, using SSH with docker and letting the container running all the time in detached mode is a way to work around the docker support.

    But this is not the way how docker normally works. normally there is no ssh running in the container, and interaction with the container happens with docker copy and the docker only runs as long as there is a task to do eg compile, generate CMake or debug. run the application.

    I would like to know is there a plan to make more native docker support rather than a workaround with the remote development over ssh.

    • Anastasia Kazakova

      Anastasia Kazakova says:

      January 21, 2020

      Yes, the proper Docker support is planned under this request: It’s considered for some 2020.x release.

    • Avatar

      Jan Hudec says:

      April 21, 2020

      Devcontainers (in VSCode) do run all the time in detached mode, otherwise parallel processes wouldn’t see each other. But it does correctly use docker exec to spawn things.

  5. Avatar

    Ernest says:

    January 21, 2020

    That would be very useful only if the remote development would work. Foreseeing the next comment with “Remote dev works in CLion”, no it isnt, when syncing any decent project takes 4 hours I call it broken.

    • Anastasia Kazakova

      Anastasia Kazakova says:

      January 21, 2020

      We would be happy to investigate what’s going on there for you. Could you please create a ticket in YouTrack and attach an IDE log to start with?

  6. Avatar

    Niklas says:

    January 27, 2020

    When proper Docker support is available, will it just work with Conan?

    • Anastasia Kazakova

      Anastasia Kazakova says:

      January 27, 2020

      I suppose it’s possible. But we’ll definitely have to double check

      • Avatar

        Niklas says:

        January 28, 2020

        Ok it would be great if this worked seamlessly somehow. My experience using remote development and synchronise the conan data was less than ideal.

  7. Avatar

    Olaf says:

    April 15, 2020

    Thanks for the writeup. I’m using it as follows:

    docker run -d –cap-add sys_ptrace -p127.0.0.1:2222:22 –name company-debian9 –volume /home/hostuser:/home/containerusr –volume /home/hostuser/.ccache:/ccache myuser/company-debian9

    Please note the following:

    * I use a volume to map the data into the container. User IDs in container and on host are the same
    * CLion is configured not do deploy via SSH, but to the given shared folder

    Yet, the toolchain keeps forgetting the credentials and the deployment settings keep changing. Each time I configure it, a new entry in “Deployment” appears. Same toolchain name, different UUID (or whatever it is) in braces. And it defaults to SFTP.

  8. Avatar

    regularjack says:

    May 6, 2020

    In case anyone’s interested in a prebuilt image for this, I have one setup here:

Discover more