Chapters

Hide chapters

Server-Side Swift with Vapor

Third Edition · iOS 13 · Swift 5.2 - Vapor 4 Framework · Xcode 11.4

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section I: Creating a Simple Web API

Section 1: 13 chapters
Show chapters Hide chapters

33. Deploying with Docker
Written by Tim Condon

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Docker is a popular containerization technology that has made a huge impact in the way applications are deployed. Containers are a way of isolating your applications, allowing you to run multiple applications on the same server.

Using a container, instead of a full-fledged virtual machine, allows your containerized applications to share more of the host machine’s resources. In turn, this leaves more resources for your application to use rather than consuming them to support the virtual machine itself.

Docker can run almost anywhere, so it provides a good way to standardize how your application should run, from local testing to production.

Note: If you need a refresher on Docker terminology — concepts such as containers and images — check out our Docker tutorial at https://www.raywenderlich.com/9159-docker-on-macos-getting-started.

Docker Compose

This chapter will also show you how to use Docker Compose. Docker Compose is a way to specify a list of different containers that work together as a single unit. These containers share the same virtual network, making it simple for them cooperate with each other.

For example, with Docker Compose, you can spin up both your Vapor app and a PostgreSQL database instance with just one command. They can communicate with each other but are isolated from other instances running on the same host.

Setting up Vapor and PostgreSQL for Development

Begin by setting up a simple development configuration to test your app in a Linux environment. To facilitate debugging any problems that arise, this will be a much simpler configuration than you’ll use in production.

#1
FROM swift:5.3
#2
WORKDIR /app
#3
COPY . .
#4
RUN swift package clean
RUN swift build -c release  --enable-test-discovery
RUN mkdir /app/bin
RUN mv `swift build -c release --show-bin-path` /app/bin
EXPOSE 8080
#5
ENTRYPOINT ./bin/release/Run serve --env local \
  --hostname 0.0.0.0
# 1
version: '3'
# 2
services:
  # 3
  til-app:
    # 4
    depends_on:
      - postgres
    # 5
    build:
      context: .
      dockerfile: develop.Dockerfile
    # 6
    ports: 
      - "8080:8080"
    environment:
      - DATABASE_HOST=postgres
      - DATABASE_PORT=5432
  # 7
  postgres:
    # 8
    image: "postgres"
    # 9
    environment:
      - POSTGRES_DB=vapor_database
      - POSTGRES_USER=vapor_username
      - POSTGRES_PASSWORD=vapor_password

  # 10
  start_dependencies:
    image: dadarek/wait-for-dependencies
    depends_on:
      - postgres
    command: postgres:5432
# 1
docker-compose -f docker-compose-develop.yml build
# 2
docker-compose -f docker-compose-develop.yml run --rm start_dependencies
# 3
docker-compose -f docker-compose-develop.yml up til-app
docker-compose -f docker-compose-develop.yml down
docker volume prune -f

Setting up Vapor and PostgreSQL for Production

There are several changes you can make to your Docker configuration to simplify managing your app in a production environment. In this section, you’ll split your app into a “builder” container and a production image. You’ll also configure the PostgreSQL container to save its database in your host’s file system. This makes your data persist across changes to your app and its configuration.

# 1
FROM swift:5.3-focal as build

# 2
RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
    && apt-get -q update \
    && apt-get -q dist-upgrade -y \
    && rm -rf /var/lib/apt/lists/*

# 3
WORKDIR /build

# 4
COPY ./Package.* ./
RUN swift package resolve

# 5
COPY . .
RUN swift build --enable-test-discovery -c release

# 6
WORKDIR /staging
RUN cp "$(swift build --package-path /build -c release \
    --show-bin-path)/Run" ./
RUN [ -d /build/Public ] && \
    { mv /build/Public ./Public && chmod -R a-w ./Public; } \
    || true
RUN [ -d /build/Resources ] && \
    { mv /build/Resources ./Resources && \
    chmod -R a-w ./Resources; } || true

# 7
FROM swift:5.3-focal-slim

# 8
RUN export DEBIAN_FRONTEND=noninteractive \
    DEBCONF_NONINTERACTIVE_SEEN=true && \
    apt-get -q update && \
    apt-get -q dist-upgrade -y && \
    rm -r /var/lib/apt/lists/*

# 9
RUN useradd --user-group --create-home --system \
    --skel /dev/null --home-dir /app vapor
# 10
WORKDIR /app
# 11
COPY --from=build --chown=vapor:vapor /staging /app
# 12
USER vapor:vapor
# 13
EXPOSE 8080
# 14
ENTRYPOINT ["./Run"]
CMD ["serve", "--env", "production", "--hostname", 
     "0.0.0.0", "--port", "8080"]
# 1
version: '3.7'

# 2
volumes:
  db_data:

# 3
x-shared_environment: &shared_environment
  LOG_LEVEL: ${LOG_LEVEL:-debug}
  DATABASE_HOST: db
  DATABASE_NAME: vapor_database
  DATABASE_USERNAME: vapor_username
  DATABASE_PASSWORD: vapor_password

# 4
services:
  # 5
  app:
    # 6
    image: tilapp:latest
    # 7
    build:
      context: .
    # 8
    environment:
      <<: *shared_environment
    # 9
    depends_on:
      - db
    # 10
    ports:
      - '8080:8080'
    # 11
    command: ["serve", "--env", "production", "--hostname", 
              "0.0.0.0", "--port", "8080"]
  # 12
  db:
    # 13
    image: postgres:12-alpine
    # 14
    volumes:
      - db_data:/var/lib/postgresql/data/pgdata
    # 15
    environment:
      PGDATA: /var/lib/postgresql/data/pgdata
      POSTGRES_USER: vapor_username
      POSTGRES_PASSWORD: vapor_password
      POSTGRES_DB: vapor_database
    ports:
      - '5432:5432'
docker stop postgres
docker-compose build
docker-compose up -d db
docker-compose up app

Where to go from here?

You’ve seen some basic recipes for how to run your app in a Docker environment. Because Docker is so flexible, these recipes only scratch the surface of the possibilities available to you. For example, you might want to allow your app to save uploaded files in the host’s file system. Or, you might want to configure the app to run behind an Nginx proxy server to get secure HTTPS access.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now