Even though Cargo makes it a breeze to work with different toolchains and dependencies, having an isolated environment can be a great asset. With Dev Containers this is easier than ever. I've been learning both Rust and Docker recently, so combining them here felt like a logical move. In this article I would like to share all steps I went through and some of the pitfalls I encountered.
First I will show the basics of how to run Rust code in a normal Docker container to get a feel for the process. Then I'll explain how to get a Dev Container running that will mount a selected VS Code folder into the container. Starting a new Rust project is then essentially the same as running Rust locally.
Finally I will show how to set up the container to run Rustlings so you can have a nicely containerized environment to do some Rust learning.
Docker
This step is not too complicated. Basically follow the instructions here: https://docs.docker.com/desktop/
I would recommend installing Docker Desktop to get started and following some of the basic tutorials inside the application. This will show you how to set up some basic containers in different ways to build intuition about how Docker works.
Also, think about where you want to store your Docker Images, Docker needs a space to save Images downloaded from the web and created by you. This location can get quite large, so make sure to select a storage medium with enough space. You can select this from the Settings ⇒ Resources ⇒ Advanced
menu. Of course, removing unused Images will help with this too.
Make sure to get a bit of a feeling with the docker command line interface commands too. The in-app tutorial already shows a few of these commands, this will help a lot with troubleshooting.
Rust inside a container
Docker has some nice tutorials using different languages. But most of them go quite deep very quickly. There is also no need for me to reproduce any of them here since Docker has already taken care of the details.
Instead I would like to show the absolute minimal setup needed for running a Rust application inside a Docker container. You can skip this part if you are already familiar with Docker basics.
Setting up a minimal Rust project
Since we assume here that Rust is not installed, we have to set up our project manually. For which we will use the following folder structure:
docker-rust-hello-world
| src
| - main.rs
| Cargo.toml
| Dockerfile
With the following contents
Cargo.toml
[package]
name = "hello-world"
edition = "2024"
Main.rs
fn main() {
println!("Hello World!");
}
Normally Cargo would take care of this, and later inside the Dev Container we will be able to use Cargo.
Initialize the docker project
We could use docker init
here, but we only need the Dockerfile, so we might as well create that manually too. Add the following contents to Dockerfile
:
FROM rust:latest
COPY ./ ./
RUN cargo build --release
CMD [ "./target/release/hello-world" ]
This will grab the rust:latest
image and use it as a base for our image. Then the files we created before will be copied to the image. Using the Rust toolchain already available on the image we will then use cargo to build our application. Finally, we define which command should be run at the end. In this case this starts the application that was just built.
Build and run
Now all we need to do is run docker build -t rs-hello-world
in the folder we just built. This will create a new container named "rs-hello-world", based on the Dockerfile. If any errors pop up, you can check the build process in Docker Desktop or the terminal output for more details. Make sure the container was created by using docker images
or looking in Docker Desktop.
Finally, start the container using docker run rs-hello-world:latest
this should start the container and print Hello World!
to the terminal. Equivalently, you can do this from Docker Desktop and see the result in the Container Logs.
A Basic Rust Dev Container
With the Dev Containers Extension we can connect to a Docker container directly from VS Code. To set it up, simply follow these steps:
- Start by installing the Dev Containers extension inside VS Code. You can also make a separate profile just for using Dev Containers with only this extension. Additional extensions will be installed into the Dev Container environment and will be managed separately.)
- Open a new folder in VS Code, for this tutorial make a new empty folder and select that one. But in general this can be an existing folder with source code.
- Open the Command Palette (
Ctrl+Shift+P
) and type in "Dev Containers: Open folder in container..." - This will start the configurator to set up Dev Container. Choose the following options
- Add configuration to workspace
- Configuration template: Rust (devcontainers)
- bullseye
- No additional features
- dependabot is unimportant for this tutorial
- Now, if everything went correctly, VS Code should build and start the container. You can see detailed information about the build process inside Docker Desktop if you want.
The Dev Containers extension has created a .devcontainers/devcontainer.json
file inside the project directory. You can freely edit this for more complex configurations. And it can be combined with a Dockerfile for even more customization. For now this basic setup is enough to get started.
In the lower left of the VS Code window it should now show that you are connected with Dev Containers. Open a terminal and check the version of cargo with cargo --version
. Which should now work, even if there is no local installation of Rust. If you now want to start developing your own Rust applications, this is where you can stop. The next section installs Rustlings and shows some more useful features of Dev Containers.
Adding Rustlings
Rustlings is a fantastic Rust learning platform built with Rust itself. It is a cargo package which has code in it with a bunch of mistakes which you will have to solve. The package also includes a CLI application which will provide progress tracking and hints.
Installing Rustlings can be done manually after container creation, but Dev Containers provide some very useful features for adding commands to the build pipeline using the .devcontainers.json
file. In this case I want a container that preconfigures Rustlings and automatically runs the CLI application whenever the container is started. To get this behavior we only need to add the following lines to .devcontainers.json
:
"onCreateCommand": {
"install_rustlings": "cargo install rustlings"
},
"postCreateCommand": {
"init_rustlings": "yes \"\" | rustlings init"
},
postAttachCommand": "cd rustlings && rustlings --manual-run"
After this change you'll need to rebuild the container, which can be done from the command palette (Ctrl+Shift+P) => "Dev Containers: Rebuild Container". This should start the rebuild process and drop you right into Rustlings
Expanding the environment
One common use case is to do development using a Dev Container and then modify the first part of this tutorial to run the application developed in the Dev Container environment in a standalone Docker container. Of course then there are practical matters to consider (for example using an incremental build step where an intermediary container is used to build the application) and more features to add (like volumes for data storage and networking options). But this falls outside the scope of this tutorial.
You can also modify the .devcontainer.json
file to work with Dockerfile
s and compose.yaml
files. But for basic configuration this is rarely necessary due to the extensive options in the json specification.
It is also possible to add additional storage by using Docker Volumes. Something I want to discuss in another article about using the game development framework Bevy in a Dev Container.
Files
All necessary project files can be found here.
Additional notes and resources
- I use a WSL backend and there seems to be some issues with file watching (You can read more about it here and here). This means that automatic recompilation on save does not work, this is why I add the
--manual-run
flag in the initialization. On other systems this might work without issues. - Rebuilding the container after already building it once with Rustlings will give an error. This is because the rustlings folder already exists and the
rustlings init
command will fail. If you haven't done any work you can just remove the rustlings folder (it should be in the workspace folder you chose in step 2 while initializing your Dev Container). Or you can modify the.devcontainer.json
file to remove the postCreateCommand that initializes Rustlings. - This is my first article I publish here (first article in general tbh) and I hope to expand this a lot in the near future. I'm mostly self-taught in regards to Rust and Docker so if there are any inconsistencies please let me know and I will correct them. I'm always happy to receive feedback and/or requests, so please do so in the comments.
Top comments (0)