I enjoy playing around with a variety of tools and languages. Installing packages on multiple machines became a pain, so I learned Docker to utilize containers and create images with the those packages. This keeps my machines clean and gives me a portable setup for development.
So now I just follow these steps with every project:
- Create/Use a Dockerfile to create an image with all the necessary packages
- Create a
docker-compose.yml
file in the project directory that uses the image - Add project-specific settings in the compose file (ports, volumes, etc.)
These are just rules and naming conventions I set for myself to follow
The Dockerfiles are meant to be generic for a development environment. That's why I start from an Ubuntu image,
and install the appropriate packages there. Why not start from something like node:latest
? Maybe I want to use
Yarn, Bun, etc. or all of them at the same time. It's just much easier to start from a bare bones Ubuntu image.
Things to keep in mind with writing the Dockerfile:
- All commands are ran as
root
and files created will be owned byroot
- Note where packages install things and which envnironment variables they use
- Create a new user with a
$UID:$GID
that matches the host system user's$UID:$GID
- Add any folders that will be used in the compose file's volume binds (they will be owned by root if created in compose)
- Update
$PATH
or link package binaries to/bin
- Copy any user-specific configuration files (like
.bashrc
) into the new user's Home - Make sure the new user is the owner of their Home directory
- Switch from root to the new user
The image name follows the format env_$ENVIRONMENT
, for example env_webdev
.
Building the image:
docker build -t $ENV_NAME:$VERSION -f $DOCKERFILE .
# Get more detailed logs during a build.
docker build -t $ENV_NAME:$VERSION -f $DOCKERFILE . --progress=plain
Each project will have a docker-compose.yml
file for project-specifc settings.
Things to keep in mind with writing the compose file:
- Container name is prefixed with
project_
orwork_
- Use locally built image with
pull_policy: missing
- Ensure I can drop into a bash session with
stdin_open: true
andtty: true
- Copy project files into the user's Home folder
- Volume binds to non-existent folders (like ~/.cache) will be created and owned by root, so ensure they are already created during the image build
Bare minimum compose file:
services:
$PROJECT:
container_name: project_$PROJECT
image: env_$IMAGE
pull_policy: missing
stdin_open: true
tty: true
volumes:
- ./:/home/$USER/app/
Running the container:
docker compose up -d
docker exec -it $CONTAINER_NAME bash --login
Running
bash --login
in the container ensures that something like.bashrc
gets sourced.