Friday, July 15, 2022

Updated Dev Container Setup for Swift 5.7 and Vapor

As mentioned previously, Swift 5.7 (currently in beta) looks to be another significant release. I’ve written previously about using the Dev Containers feature of Visual Studio Code to do server-side Swift development. The setup then was rather complicated as there were no pre-built Docker containers for the then-current beta version of Swift, 5.5. More recently, I’ve been curious to give Swift 5.7 a try, so I wanted to update the Dev Container configuration I was using previously. The good news now is that pre-built containers of the Swift 5.7 beta are available, so the setup is significantly simpler.

As before, we’ll start with the dev container configuration file itself:


    "name": "{{ project name }}",
    "dockerComposeFile": "docker-compose.yml",
    "service": "app",
    "workspaceFolder": "/workspace",
    "settings": {
        "lldb.library": "/usr/lib/"
    "extensions": [
    "forwardPorts": [
        {{ your project’s preferred port, default 8080 for Vapor }},
        {{ your database’s port, default 5432 for PostgreSQL }}
    "remoteUser": "vscode"

This differs from the dev container template for Swift because we are going to use a docker-compose.yml file. Using docker-compose will allow us to bring up a database container at the same time as the rest of our project. Note also that we are using a different Swift extension than last time. This is the new extension provided by the Swift Server Work Group that I mentioned in my last post.

Here, then, is the corresponding docker-compose file:


version: '3.8'
      context: ..
      dockerfile: .devcontainer/Dockerfile
        NODE_VERSION: "none"
      - ..:/workspace:cached
    command: sleep infinity
    network_mode: service:db
    env_file: .env
    privileged: true
    image: postgres:14.4
    restart: unless-stopped
      - postgres-data:/var/lib/postgresql/data
      - ./db-scripts:/docker-entrypoint-initdb.d
      POSTGRES_USER: {{ your preferred DB user }}
      POSTGRES_DB: {{ your preferred DB name }}
      POSTGRES_PASSWORD: {{ your DB user’s password }}
  postgres-data: null

There are a few things to note here. First, if you would like to have a version of Node available (presumably for front-end development), replace the “none” in NODE_VERSION: "none" with your preferred version. Next, note the inclusion of an environment variable definition file (.env); this isn’t necessary but can be handy. Next, note the privileged: true line; this is necessary for some of LLDB’s functionality (including the Swift REPL) and should never be left in a production configuration. Finally, note the ./db-scripts:/docker-entrypoint-initdb.d line; this allows us to insert startup scripts into the PostgreSQL container. This is specific to PostgreSQL’s Docker container configuration so if you are using a different database it won’t work. I’ve added it in order to automatically create a test database for use in running the automated test suite. The directory referenced, db-scripts, contains a single file:


set -e
set -u
echo "  Creating test database."
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
    CREATE DATABASE {{ your preferred DB name }}_test;
    GRANT ALL PRIVILEGES ON DATABASE {{ your preferred DB name }}_test TO {{ your preferred DB user }};

Now on to the Dockerfile:


FROM swiftlang/swift:nightly-5.7-focal
COPY .devcontainer/library-scripts/ /tmp/library-scripts/
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
    && /bin/bash /tmp/library-scripts/ "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \
    && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* && rm -rf /tmp/library-scripts
ENV NVM_DIR=/usr/local/share/nvm
COPY .devcontainer/library-scripts/ /tmp/library-scripts/
RUN bash /tmp/library-scripts/ "${NVM_DIR}" "${NODE_VERSION}" "${USERNAME}" \
    && rm -rf /var/lib/apt/lists/* /tmp/library-scripts
COPY .devcontainer/library-scripts/ /tmp/library-scripts/
RUN bash /tmp/library-scripts/ \
    && rm -rf /tmp/library-scripts

This Dockerfile references three scripts in a library-scripts directory. Two of these come directly from the standard dev container template for Swift, and The third is a simple script to install the Vapor toolbox; it is completely optional and you can skip it if you like. Here is that script:


cd /opt
git clone
cd toolbox
git checkout "{{ current toolbox version }}"
make install