Development container for the weblog

I set up a development environment for the weblog using a Docker container. The inspiration was the excellent collection of user-friendly containerized applications from linuxserver.io. They even provide complete operating systems with integrated desktop environments, such as Webtop.

One standout container is their code-server, a browser based version of Microsoft VSCode. Since I work on my weblog from different locations, using a code-server based container would allow me to streamline development by eliminating the need to install dependencies on multiple machines.The container should support the full development cycle: retrieving the code from GitHub, editing, debugging in the gui, and pushing updates back to GitHub, which triggers automatic deployment on Cloudflare Pages.

Setting Up the Docker Container

I started by creating a Dockerfile based on the code-server container:

FROM lscr.io/linuxserver/code-server:latest

WORKDIR /config/workspace

I then added a compose.yaml file as per the code-server instructions:

services:
  weblog:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: weblog
	volumes:
      - weblog-data:/config
    environment:
      - PUID=0
      - PGID=0
      - TZ=Europe/Amsterdam
      - DEFAULT_WORKSPACE=/config/workspace
    ports:
      - 8443:8443
    restart: unless-stopped

volumes:
  weblog-data:

I used a named volume for the working directory such that it is persistent in between container instances.

Adding Node.js for Astro Framework

Since Astro is a Node.js framework, I needed to install Node.js in the container. I could have used linuxserver.io’s mod system, in particular, the NodeJS mod. However, using a mod would repeat the installation on each startup of the container. I decided to install Node.js during container build time and added the following to the Dockerfile:

ARG NODE_VERSION

# Install Nodejs
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | sudo -E bash - && \
    apt install -y nodejs

To compose.yaml I added:

      args:
        - NODE_VERSION=20

Cloning Weblog Code with GitHub CLI

To clone the private weblog GitHub repository, I installed the Github CLI in the container using the following Dockerfile instructions:

RUN (type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) \
	&& sudo mkdir -p -m 755 /etc/apt/keyrings \
	&& wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
	&& sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
	&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
	&& sudo apt update \
	&& sudo apt install gh -y

To authenticate with Github, the GH_TOKEN environment variable can be used, or the authentication can be done manually via gh auth login. I added the environment variable to the compose.yaml such that it is copied from the host if it exists and ignored otherwise:

    environment:
      - GH_TOKEN

The init.sh script simplifies the setup process and is included in the container

#!/bin/bash

if [ -z "$GH_TOKEN" ]; then
  echo "GH_TOKEN is not set. Authenticating..."
  gh auth login || { echo "Authentication failed."; exit 1; }
else
  echo "GH_TOKEN is set. Skipping manual authentication."
fi

gh repo clone https://[email protected]/XXX/weblog

cd weblog
npm install
npm run astro telemetry disable

The dos2unix utility converts the script to avoid issues with Windows-style line endings:

RUN apt update && \
    apt install -y wget sudo git vim curl unzip dos2unix

COPY init.sh .

RUN dos2unix init.sh && chmod a+x init.sh

Installing VSCode Extensions for Astro

To enable Astro support in code-server, I used linuxserver.io’s mod system with the Extension Arguments mod to install the Astro extension inside code-server. I updated compose.yaml with:

    environment:
      - DOCKER_MODS=linuxserver/mods:code-server-extension-arguments
      - VSCODE_EXTENSION_IDS=astro-build.astro-vscode

Running the Weblog in Development Mode

A port mapping is added in compose.yaml to access the weblog locally from outside the container when run in development mode, :

    ports:
      - 4321:4321

By default the weblog running in development mode is only made available on the localhost interface. Hence it is not globally (i.e. outside the container) available. To make the development server listen on all interfaces, I modified the astro.config.mjs file in the Astro project:

export default defineConfig({
  ...
  server: {
    host: true
  },
});

Complete Docker and Compose files

Finally, here is the complete Dockerfile:

FROM lscr.io/linuxserver/code-server:latest

WORKDIR /config/workspace

# Install some utilities
RUN apt update && \
    apt install -y wget sudo git vim curl unzip dos2unix

COPY app/* .

# Prepare scripts
RUN dos2unix init.sh && \
    chmod a+x init.sh

ARG NODE_VERSION

# Install Nodejs
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | sudo -E bash - && \
    apt install -y nodejs

# Install Github client
RUN (type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) \
	&& sudo mkdir -p -m 755 /etc/apt/keyrings \
	&& wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
	&& sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
	&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
	&& sudo apt update \
	&& sudo apt install gh -y

And the compose.yaml:

services:

  weblog:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        - NODE_VERSION=20
    container_name: weblog
    volumes:
      - weblog-data:/config
    environment:
      - DOCKER_MODS=linuxserver/mods:code-server-extension-arguments
      - VSCODE_EXTENSION_IDS=astro-build.astro-vscode
      - GH_TOKEN
      - PUID=0
      - PGID=0
      - TZ=Europe/Amsterdam
      - DEFAULT_WORKSPACE=/config/workspace
    ports:
      - 8443:8443
      - 4321:4321
    restart: unless-stopped

volumes:
  weblog-data: