Dockerizing Bridgetown

What is Bridgetownrb?

Bridgetownrb  is a "Webpack-aware,
Ruby-powered static site generator
for the modern Jamstack era."


So what does this mean? To me it means it is a static site generator that uses Webpack under the hood and can source data from other places like a CMS or markdown files just like other static site generators such as Gatsby or Gridsome

Table Of Contents

Note:

If you would like to skip straight to building without explanations feel free to go to the I know what I’m doing section.

Prerequisites

There are only 2 prerequisites for this project.

Docker & Docker Compose. To check you have them, run the following:

Bash
docker -v
# Docker version 19.03.6, build 369ce74a3c

docker-compose -v
# docker-compose version 1.25.0, build unknown

Create a new directory

Now that we’ve confirmed Docker and Docker Compose are installed, lets setup the initial structure for Docker to pull down Bridgetownrb so we do not have to install it locally.

Bash
mkdir -p bridgetown-project
cd bridgetown-project

Docker Files

Adding a Dockerfile

I’m not goin to go too in depth into this Dockerfile, but the point of it is to be able to run a Docker container as a non-root user and still do everything you need to do. We’ll be using Alpine Linux to keep the image small.

Create a Dockerfile and add the following contents into it.

```dockerfile title=Dockerfile FROM ruby:2.6-alpine3.11 as builder

RUN apk add –no-cache –virtual
# # required nodejs-dev yarn bash
tzdata build-base libffi-dev
# # nice to haves curl git
# # Fixes watch file isses with things like HMR libnotify-dev

FROM builder as bridgetownrb-app

This is to fix an issue on Linux with permissions issues

ARG USER_ID=${USER_ID:-1000} ARG GROUP_ID=${GROUP_ID:-1000} ARG DOCKER_USER=${DOCKER_USER:-user} ARG APP_DIR=${APP_DIR:-/home/user/bridgetown-app}

Create a non-root user

RUN addgroup -g $GROUP_ID -S $GROUP_ID RUN adduser –disabled-password -G $GROUP_ID –uid $USER_ID -S $DOCKER_USER

Create and then own the directory to fix permissions issues

RUN mkdir -p $APP_DIR RUN chown -R $USER_ID:$GROUP_ID $APP_DIR

Define the user running the container

USER $USER_ID:$GROUP_ID

. now == $APP_DIR

WORKDIR $APP_DIR

COPY is run as a root user, not as the USER defined above, so we must chown it

COPY –chown=$USER_ID:$GROUP_ID Gemfile* $APP_DIR/ RUN gem install bundler RUN bundle install

For webpacker / node_modules

COPY –chown=$USER_ID:$GROUP_ID package.json $APP_DIR COPY –chown=$USER_ID:$GROUP_ID yarn.lock $APP_DIR

RUN yarn install

CMD [“yarn”, “start”]


[Reference File on
Github](https://github.com/ParamagicDev/getting-started-with-bridgetown/blob/prior-bridgetown-new/Dockerfile)

<h3 id="adding-docker-compose">
  <a href="#adding-docker-compose">Adding a docker-compose.yml</a>
</h3>

Now that we have a Dockerfile as our base, lets make it easy to call the
Dockerfile without having to specify a bunch of build arguments.

Create a `docker-compose.yml` and add the following content:

```yaml
# docker-compose.yml

version: '3'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        USER_ID: ${USER_ID:-1000}
        GROUP_ID: ${GROUP_ID:-1000}
        DOCKER_USER: ${DOCKER_USER:-user}
        APP_DIR: ${APP_DIR:-/home/user/bridgetown-app}

    command: bash -c "yarn start --host '0.0.0.0'"

    ports:
      - '4000:4000'
      # Not totally necessary to open 4001, but it is used, so lets make it discoverable
      - '4001:4001'
      - '4002:4002'

    volumes:
      - .:${APP_DIR:-/home/user/bridgetown-app}
      # this seperates node_modules from the host
      - node_modules:${APP_DIR:-/home/user/bridgetown-app}/node_modules

volumes:
  node_modules:

Reference File on Github

Adding docker.env

You’ll notice above that theres a bunch of ENV variables being used to substitute values. Now there’s a few ways to provide the ENV variables to Docker. I’ve found the easiest way to pass ENV variables is by sourcing a file with ENV variables.

To show you what this looks like lets create a ‘docker.env’ file.

```bash title=docker.env

Assign and export seperately to avoid masking return values.

USER_ID=$(id -u “$USER”) GROUP_ID=$(id -g “$USER”) export USER_ID export GROUP_ID

export DOCKER_USER=”user” export APP_DIR=”/home/$DOCKER_USER/bridgetown”


Now in order to pull these values into your shell environment run the
following command:

```bash
source ./docker.env

This will now pull in your ENV variables for docker to use.

Note:

This is really only necessary for Linux users. Mac and Windows users should be fine to run without this script. It has not been tested however.

Adding .dockerignore

The final piece to this Docker puzzle is to create a .dockerignore. I stole the .gitignore provided by Bridgetownrb for this. It looks as follows:

```bash title=.dockerignore

Bridgetown

output .bridgetown-cache .bridgetown-metadata .bridgetown-webpack

Dependency folders

node_modules bower_components vendor

Caches

.sass-cache .npm .node_repl_history

Ignore bundler config.

/.bundle

Ignore Byebug command history file.

.byebug_history

dotenv environment variables file

.env

Mac files

.DS_Store

Yarn

yarn-error.log yarn-debug.log* .pnp/ .pnp.js

Yarn Integrity file

.yarn-integrity

.git


[Reference File on
Github](https://github.com/ParamagicDev/getting-started-with-bridgetown/blob/prior-bridgetown-new/.dockerignore)

<h2 id="dep-files">
  <a href="#dep-files">Dependency Files</a>
</h2>

<h3 id="adding-gemfile">
  <a href="#adding-gemfile">Adding a Gemfile</a>
</h3>

Alright, with the Docker setup above, we can now specify our
dependency files.

The first step is to create a `Gemfile`. Create a Gemfile as follows:

```ruby title=Gemfile


source "https://rubygems.org"
gem "bridgetown", "~> 0.15.0"

This will tell bundler to install bridgetown from Rubygems.org

Adding a package.json

Create a package.json structured similarly to the one below:

```json title=package.json { “name”: “bridgetown-site”, “version”: “1.0.0”, “private”: true }


<h3 id="adding-lockfiles">
  <a href="#adding-lockfiles">Adding lockfiles</a>
</h3>

Almost done with the setup I promise!

Finally, lets create 2 empty lockfiles.

The 2 lockfiles are `yarn.lock` and `Gemfile.lock`

```bash
touch yarn.lock Gemfile.lock

Generating a project

File structure prior to generation

Your file structure should look as follows if you followed the above steps.

Bash
tree -L 1 -a .

.
├── docker-compose.yml
├── docker.env
├── Dockerfile
├── .dockerignore
├── Gemfile
├── Gemfile.lock
├── package.json
└── yarn.lock

Reference Branch on Github

Running the Generation Command

Bash
source ./docker.env && docker-compose run --rm bridgetown new . --force

This will generate a new project for bridgetown

File Structure After Generation

Bash
tree -L 1 -a .

.
├── bridgetown.config.yml
├── docker-compose.yml
├── docker.env
├── Dockerfile
├── .dockerignore
├── frontend
├── Gemfile
├── Gemfile.lock
├── .git
├── .gitignore
├── package.json
├── plugins
├── README.md
├── src
├── start.js
├── sync.js
├── webpack.config.js
└── yarn.lock

Reference Branch on Github

Now, to start your server you can simply run:

Bash
source ./docker.env && docker-compose up --build

This will allow you to view Bridgetown welcome screen on localhost:4000

Useful Commands

Starting the server

If it’s your first time since generating the project, run

Bash
source ./docker.env && docker-compose up --build

If you have already built the container, you can simply do:

Bash
source ./docker.env && docker-compose up

Stopping the server

In another terminal to stop the server you can simply run:

Bash
docker-compose down --remove-orphans

Other commands

Sourcing ENV variables


This is only technically required once in a running terminal.
source ./docker.env

Run a command in an already running container:


docker-compose exec web [command]

Run a one-off command:


docker-compose run --rm web [command]

Upgrading package.json:

Bash
docker-compose run --rm web yarn upgrade
docker-compose down --remove-orphans
docker-compose up --build

Adding an npm package:

Bash
docker-compose run --rm web yarn add [package]
docker-compose down --remove-orphans
docker-compose up --build

Adding a gem

Bash
docker-compose run --rm web bundle add [gem]
docker-compose down --remove-orphans
docker-compose up --build

The below is a TLDR / reference version of the above. To skip to the links sections click the link below.


Links section

I know what I'm doing

Bash
mkdir -p bridgetown-project && cd bridgetown-project
touch Gemfile Gemfile.lock package.json yarn.lock \\
      .dockerignore docker-compose.yml Dockerfile docker.env

```dockerfile title=Dockerfile

FROM ruby:2.6-alpine3.11 as builder

RUN apk add –no-cache –virtual
# # required nodejs-dev yarn bash
tzdata build-base libffi-dev
# # nice to haves curl git
# # Fixes watch file isses with things like HMR libnotify-dev

FROM builder as bridgetownrb-app

This is to fix an issue on Linux with permissions issues

ARG USER_ID=${USER_ID:-1000} ARG GROUP_ID=${GROUP_ID:-1000} ARG DOCKER_USER=${DOCKER_USER:-user} ARG APP_DIR=${APP_DIR:-/home/user/bridgetown-app}

Create a non-root user

RUN addgroup -g $GROUP_ID -S $GROUP_ID RUN adduser –disabled-password -G $GROUP_ID –uid $USER_ID -S $DOCKER_USER

Create and then own the directory to fix permissions issues

RUN mkdir -p $APP_DIR RUN chown -R $USER_ID:$GROUP_ID $APP_DIR

Define the user running the container

USER $USER_ID:$GROUP_ID

. now == $APP_DIR

WORKDIR $APP_DIR

COPY is run as a root user, not as the USER defined above, so we must chown it

COPY –chown=$USER_ID:$GROUP_ID Gemfile* $APP_DIR/ RUN gem install bundler RUN bundle install

For webpacker / node_modules

COPY –chown=$USER_ID:$GROUP_ID package.json $APP_DIR COPY –chown=$USER_ID:$GROUP_ID yarn.lock $APP_DIR

RUN yarn install

CMD [“yarn”, “start”]


```yaml title=docker-compose.yml
version: '3'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        USER_ID: ${USER_ID:-1000}
        GROUP_ID: ${GROUP_ID:-1000}
        DOCKER_USER: ${DOCKER_USER:-user}
        APP_DIR: ${APP_DIR:-/home/user/bridgetown-app}

    command: bash -c "yarn start --host '0.0.0.0'"

    ports:
      - '4000:4000'
      # Not totally necessary to open 4001, but it is used, so lets make it discoverable
      - '4001:4001'
      - '4002:4002'

    volumes:
      - .:${APP_DIR:-/home/user/bridgetown-app}
      # this seperates node_modules from the host
      - node_modules:${APP_DIR:-/home/user/bridgetown-app}/node_modules

volumes:
  node_modules:

```bash title=docker.env

Assign and export seperately to avoid masking return values.

USER_ID=$(id -u “$USER”) GROUP_ID=$(id -g “$USER”) export USER_ID export GROUP_ID

export DOCKER_USER=”user” export APP_DIR=”/home/$DOCKER_USER/bridgetown”


```bash title=.dockerignore


# Bridgetown
output
.bridgetown-cache
.bridgetown-metadata
.bridgetown-webpack

# Dependency folders
node_modules
bower_components
vendor

# Caches
.sass-cache
.npm
.node_repl_history

# Ignore bundler config.
/.bundle

# Ignore Byebug command history file.
.byebug_history

# dotenv environment variables file
.env

# Mac files
.DS_Store

# Yarn
yarn-error.log
yarn-debug.log*
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity

.git

```ruby title=Gemfile

Gemfile

source “https://rubygems.org” gem “bridgetown”, “~> 0.15.0”


```json title=package.json
{
  "name": "bridgetown-site",
  "version": "1.0.0",
  "private": true
}
Bash
source ./docker.env
docker-compose run --rm web bridgetown new . --force
docker-compose up --build

Navigate to localhost:4000 and bam! up and running!

Bridgetown

Bridgetownrb

Bridgetown Getting Started

Github

Github Reference Repo

Going Forward

This blog post was merely a setup blog post. My next blog post will detail creating a portfolio with TailwindCSS & Bridgetownrb.

This is a reference post to point people back to. So stay tuned for the next part of building with bridgetown.

And if you dont feel like waiting, go check out their documentation.


Bridgetown Documentation

Good luck building with Bridgetown and I hope this was useful!