Skip to main content
Container ship

Run compass, sass, guard in a Docker container

Compiling CSS inside a production web server container results in unnecessary bloat. What's the best way to use a compiled CSS workflow a la compass, in a Dockerized dev environment?

As a PHP-focused developer, I tend to ship software inside Docker containers designed for PHP. But what if I need to use tech written in Ruby, including must-use tools like Compass and Sass? Run them inside a ruby container, of course!

A few gotchas, and some fixes.

Since Compass and Sass require Ruby, the Ruby Docker container from Docker Hub seems like a good place to start. However, both the Dockerfile and our local environment require a few tweaks to work optimally as a container for running Compass and friends.

First up: File permissions. Our goal is to mount our local development directory inside the container, so we can compile CSS into it, but we'd like to keep ownership of those files. While there is some interesting work underway by the Docker core team on user sync, most Docker containers run programs as root by default. Many daemons immediately drop privileges, of course, but many containers packaged around a language, including that for Ruby, leave that up to the developer.

Our local user won't exist inside the Ruby container, but we can force the use of our local uid by specifying it at runtime.

We also want to mount our local directory into the container, and I also specify a bootstrap file to run Guard, and forward the Live Reload port.

docker-compose.yml
Code

services:
  bundler:
    image: ruby:2
    volumes:
      - ./web/themes/your-theme:/target
      - ./scripts/bundler/bootstrap.sh:/bootstrap.sh
    command:
      - /bootstrap.sh
    environment:
      - BUNDLE_PATH=vendor
      - BUNDLE_BIN=false
    ports:
      - "127.0.0.1:35729:35729" # Livereload
    user: ${USER_ID} # Set your USER_ID to maintain ownership

scripts/bundler/bootstrap.sh
Code

#!/usr/bin/env bash

cd /target
bundle install
bundle exec compass compile --force
bundle exec guard --no-interactions

The UID for our current user should be loaded in to the USER_ID variable. Compose can't interpret a command like `id -u`.

Without Docker Compose (e.g., for CI deployments)

Compiling for deployment is easy. Just run the image from the command line, and adjust your compass compile command accordingly.

Code

# docker run --rm -u $(id -u) -w "/target" --volume .:/target ruby:2 bash -c "BUNDLE_BIN=false bundle install --path vendor && bundle exec compass compile -e production --force"

Photo