Containerizing Swift Applications with Docker: A Complete Developer's Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Swift and Docker Are the Perfect Pair for Server-Side Development

\n\n

If you're used to thinking that Swift is only for iOS mobile development, it's time to be surprised. Swift is confidently moving to the server side: Vapor, Kitura, Perfect — frameworks that allow you to write fast and secure backends. But there's one problem: how to guarantee that your application will work identically on a developer's Mac and on a production server running Ubuntu?

\n\n

The answer is containerizing Swift applications with Docker. Docker allows you to package everything: the binary itself, system libraries, Swift Package Manager (SPM) dependencies — and run it anywhere. No more "it works on my machine" issues.

\n\n

In this article, we'll cover how to build a Swift application into a Docker image, optimize its size, set up multi-stage builds, and even add hot reload for development. Let's go!

\n\n

Step 1. Preparing the Swift Application

\n\n

For the example, we'll take a minimal web application using Vapor. If you don't have a project yet, create one:

\n\n
mkdir MySwiftApp && cd MySwiftApp\nswift package init --type executable
\n\n

Add Vapor to Package.swift:

\n\n
// swift-tools-version:5.7\nimport PackageDescription\n\nlet package = Package(\n    name: "MySwiftApp",\n    dependencies: [\n        .package(url: "https://github.com/vapor/vapor.git", from: "4.89.0")\n    ],\n    targets: [\n        .executableTarget(\n            name: "MySwiftApp",\n            dependencies: [\n                .product(name: "Vapor", package: "vapor")\n            ]\n        )\n    ]\n)
\n\n

The simplest main.swift:

\n\n
import Vapor\n\nvar env = try Environment.detect()\ntry LoggingSystem.bootstrap(from: &env)\nlet app = Application(env)\ndefer { app.shutdown() }\n\napp.get("hello") { req -> String in\n    return "Hello from Swift in Docker!"\n}\n\ntry app.run()
\n\n

Verify that the project compiles locally: swift build. If everything is fine, let's move on to containerization.

\n\n

Step 2. Writing a Dockerfile for Swift

\n\n

The main secret of good Swift containerization is multi-stage builds. In the first stage, we compile the binary; in the second, we copy only the binary and runtime dependencies. The final image ends up being 5-10 times smaller.

\n\n
# ---- Stage 1: Build ----\nFROM swift:5.9-jammy AS build\n\nWORKDIR /app\n\n# Copy Package.swift and Package.resolved for caching dependencies\nCOPY Package.swift ./\nCOPY Package.resolved ./\n\n# Download dependencies separately — this is cached by Docker\nRUN swift package resolve\n\n# Copy source code\nCOPY Sources ./Sources\n\n# Build in release mode\nRUN swift build -c release --static-swift-stdlib\n\n# ---- Stage 2: Runtime ----\nFROM ubuntu:jammy AS runtime\n\n# Install minimal dependencies for Swift runtime\nRUN apt-get update && apt-get install -y \\\n    libicu70 \\\n    libcurl4 \\\n    libxml2 \\\n    && rm -rf /var/lib/apt/lists/*\n\nWORKDIR /app\n\n# Copy only the binary from the build stage\nCOPY --from=build /app/.build/release/MySwiftApp .\n\n# Port that Vapor listens on by default\nEXPOSE 8080\n\nCMD ["./MySwiftApp"]
\n\n

Key points:

\n
    \n
  • --static-swift-stdlib — static linking of the standard library to avoid pulling libswiftCore.so into runtime.
  • \n
  • Separate COPY — first Package.swift, then the code. This way Docker uses layer caching and doesn't rebuild dependencies when source files change.
  • \n
  • The base image ubuntu:jammy weighs ~80 MB, the final image is about 100-150 MB.
  • \n
\n\n

Step 3. Building and Running the Container

\n\n

Build the image:

\n\n
docker build -t my-swift-app .
\n\n

Run it:

\n\n
docker run -p 8080:8080 my-swift-app
\n\n

O

Blogs

Book Recommendations