From 8f54d7057d33ccd119563cab24007c94f490585e Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Tue, 3 Mar 2026 09:30:09 -0600 Subject: [PATCH 01/12] initial copy from website content - documentation/server/guides/packaging.html --- .../Sources/ServerGuides.docc/packaging.md | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 server-guides/Sources/ServerGuides.docc/packaging.md diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md new file mode 100644 index 0000000..9fef9e8 --- /dev/null +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -0,0 +1,159 @@ +--- +redirect_from: "server/guides/packaging" +layout: page +title: Packaging Applications for Deployment +--- + +Once an application is built for production, it still needs to be packaged before it can be deployed to servers. There are several strategies for packaging Swift applications for deployment. + +## Docker + +One of the most popular ways to package applications these days is using container technologies such as [Docker](https://www.docker.com). + +Using Docker's tooling, we can build and package the application as a Docker image, publish it to a Docker repository, and later launch it directly on a server or on a platform that supports Docker deployments such as [Kubernetes](https://kubernetes.io). Many public cloud providers including AWS, GCP, Azure, IBM and others encourage this kind of deployment. + +Here is an example `Dockerfile` that builds and packages the application on top of CentOS: + +```Dockerfile +#------- build ------- +FROM swift:centos8 as builder + +# set up the workspace +RUN mkdir /workspace +WORKDIR /workspace + +# copy the source to the docker image +COPY . /workspace + +RUN swift build -c release --static-swift-stdlib + +#------- package ------- +FROM centos +# copy executables +COPY --from=builder /workspace/.build/release/ / + +# set the entry point (application name) +CMD [""] +``` + +To create a local Docker image from the `Dockerfile` use the `docker build` command from the application's source location, e.g.: + +```bash +$ docker build . -t : +``` + +To test the local image use the `docker run` command, e.g.: + +```bash +$ docker run : +``` + +Finally, use the `docker push` command to publish the application's Docker image to a Docker repository of your choice, e.g.: + +```bash +$ docker tag : /: +$ docker push /: +``` + +At this point, the application's Docker image is ready to be deployed to the server hosts (which need to run docker), or to one of the platforms that supports Docker deployments. + +See [Docker's documentation](https://docs.docker.com/engine/reference/commandline/) for more complete information about Docker. + +### Distroless + +[Distroless](https://github.com/GoogleContainerTools/distroless) is a project by Google that attempts to create minimal images containing only the application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution. + +Since distroless supports Docker and is based on Debian, packaging a Swift application on it is fairly similar to the Docker process above. Here is an example `Dockerfile` that builds and packages the application on top of a distroless's C++ base image: + +```Dockerfile +#------- build ------- +# Building using Ubuntu Bionic since its compatible with Debian runtime +FROM swift:bionic as builder + +# set up the workspace +RUN mkdir /workspace +WORKDIR /workspace + +# copy the source to the docker image +COPY . /workspace + +RUN swift build -c release --static-swift-stdlib + +#------- package ------- +# Running on distroless C++ since it includes +# all(*) the runtime dependencies Swift programs need +FROM gcr.io/distroless/cc-debian10 +# copy executables +COPY --from=builder /workspace/.build/release/ / + +# set the entry point (application name) +CMD [""] +``` + +Note the above uses `gcr.io/distroless/cc-debian10` as the runtime image which should work for Swift programs that do not use `FoundationNetworking` or `FoundationXML`. In order to provide more complete support we (the community) could put in a PR into distroless to introduce a base image for Swift that includes `libcurl` and `libxml` which are required for `FoundationNetworking` and `FoundationXML` respectively. + +## Archive (Tarball, ZIP file, etc.) + +Since cross-compiling Swift for Linux is not (yet) supported on Mac or Windows, we need to use virtualization technologies like Docker to compile applications we are targeting to run on Linux. + +That said, this does not mean we must also package the applications as Docker images in order to deploy them. While using Docker images for deployment is convenient and popular, an application can also be packaged using a simple and lightweight archive format like tarball or ZIP file, then uploaded to the server where it can be extracted and run. + +Here is an example of using Docker and `tar` to build and package the application for deployment on Ubuntu servers: + +First, use the `docker run` command from the application's source location to build it: + +```bash +$ docker run --rm \ + -v "$PWD:/workspace" \ + -w /workspace \ + swift:bionic \ + /bin/bash -cl "swift build -c release --static-swift-stdlib" +``` + +Note we are bind mounting the source directory so that the build writes the build artifacts to the local drive from which we will package them later. + +Next we can create a staging area with the application's executable: + +```bash +$ docker run --rm \ + -v "$PWD:/workspace" \ + -w /workspace \ + swift:bionic \ + /bin/bash -cl ' \ + rm -rf .build/install && mkdir -p .build/install && \ + cp -P .build/release/ .build/install/' +``` + +Note this command could be combined with the build command above--we separated them to make the example more readable. + +Finally, create a tarball from the staging directory: + +```bash +$ tar cvzf -.tar.gz -C .build/install . +``` + +We can test the integrity of the tarball by extracting it to a directory and running the application in a Docker runtime container: + +```bash +$ cd +$ docker run -v "$PWD:/app" -w /app bionic ./ +``` + +Deploying the application's tarball to the target server can be done using utilities like `scp`, or in a more sophisticated setup using configuration management system like `chef`, `puppet`, `ansible`, etc. + + +## Source Distribution + +Another distribution technique popular with dynamic languages like Ruby or Javascript is distributing the source to the server, then compiling it on the server itself. + +To build Swift applications directly on the server, the server must have the correct Swift toolchain installed. [Swift.org](/download/#linux) publishes toolchains for a variety of Linux distributions, make sure to use the one matching your server Linux version and desired Swift version. + +The main advantage of this approach is that it is easy. Additional advantage is the server has the full toolchain (e.g. debugger) that can help troubleshoot issues "live" on the server. + +The main disadvantage of this approach that the server has the full toolchain (e.g. compiler) which means a sophisticated attacker can potentially find ways to execute code. They can also potentially gain access to the source code which might be sensitive. If the application code needs to be cloned from a private or protected repository, the server needs access to credentials which adds additional attack surface area. + +In most cases, source distribution is not advised due to these security concerns. + +## Static linking and Curl/XML + +**Note:** if you are compiling with `-static-stdlib` and using Curl with FoundationNetworking or XML with FoundationXML you must have libcurl and/or libxml2 installed on the target system for it to work. From ce6cd69d0e6fdc4f11e69a0ab5704ab7100c99b4 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Tue, 3 Mar 2026 09:49:12 -0600 Subject: [PATCH 02/12] rework overview --- .../Sources/ServerGuides.docc/packaging.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index 9fef9e8..e8a1bed 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -1,14 +1,15 @@ ---- -redirect_from: "server/guides/packaging" -layout: page -title: Packaging Applications for Deployment ---- +# Packaging Swift services with containers -Once an application is built for production, it still needs to be packaged before it can be deployed to servers. There are several strategies for packaging Swift applications for deployment. +Build a container that includes your Swift service with its resources and dependencies. -## Docker +## Overview + +Web services often require configuration, dependencies, and resources in order to run correctly. +Creating an OCI container image, using technologies such as [Docker](https://www.docker.com) or [Container](https://github.com/apple/container), provides a way to deploy to your service onto a virtual machine or into a production cluster such as Kubernetes. +The container includes the executable for your service, any runtime dependencies it needs, resources the service requires, a manifest to define configuration through environment variables, expose ports to your service, and a default command to run when the container is invoked. +It combines these together into a portable image format that you run on servers. -One of the most popular ways to package applications these days is using container technologies such as [Docker](https://www.docker.com). +## Docker Using Docker's tooling, we can build and package the application as a Docker image, publish it to a Docker repository, and later launch it directly on a server or on a platform that supports Docker deployments such as [Kubernetes](https://kubernetes.io). Many public cloud providers including AWS, GCP, Azure, IBM and others encourage this kind of deployment. From 76a0438ba8cbf57d9f67966e35f50eff3795d383 Mon Sep 17 00:00:00 2001 From: Joseph Heck Date: Fri, 6 Mar 2026 12:19:55 -0600 Subject: [PATCH 03/12] Enhance packaging.md with container and build details Expanded the packaging section with details on container components, build processes, and deployment practices. --- .../Sources/ServerGuides.docc/packaging.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index e8a1bed..448d239 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -9,6 +9,28 @@ Creating an OCI container image, using technologies such as [Docker](https://www The container includes the executable for your service, any runtime dependencies it needs, resources the service requires, a manifest to define configuration through environment variables, expose ports to your service, and a default command to run when the container is invoked. It combines these together into a portable image format that you run on servers. +container: +- linux dependencies +- local filesysten with resources +- command to invokr by defauly when you "run" the container +- port that is exposed to provide access to incoming connections for listening sockets +- ENV vars preset, or added by gow you run it - config value pattern +- filesystems that your environment might provide (configmap) + + +container as layers of filesysten chnages, overlaid - creating read only layers that get loaded into a container runtime to executee your Swift swrvice in its own, isolated environment. + +your organization may provide vetted basr images, reviewed for your organizations best practices, security considerations, and so on. build on these base images to provide images that you can deploy, both locally to test and to run in production. + +simple build +dual build to slim +cache intermediate build steps to optimize build times +run locally to vet or debug +push to a registry to makr iy available fot other systems to pull it + + + + ## Docker Using Docker's tooling, we can build and package the application as a Docker image, publish it to a Docker repository, and later launch it directly on a server or on a platform that supports Docker deployments such as [Kubernetes](https://kubernetes.io). Many public cloud providers including AWS, GCP, Azure, IBM and others encourage this kind of deployment. From 6421ec4d9759cad29d6484b2033cf6d498207cf8 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 9 Mar 2026 15:47:56 -0700 Subject: [PATCH 04/12] setup --- .../Sources/ServerGuides.docc/packaging.md | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index 448d239..d4409e1 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -4,8 +4,14 @@ Build a container that includes your Swift service with its resources and depend ## Overview -Web services often require configuration, dependencies, and resources in order to run correctly. -Creating an OCI container image, using technologies such as [Docker](https://www.docker.com) or [Container](https://github.com/apple/container), provides a way to deploy to your service onto a virtual machine or into a production cluster such as Kubernetes. + + + +When you run your service in the cloud, it requires configuration, dependencies, and potentially resources to run correctly. +A common means of packaging your app and what it needs are containers. +Create an OCI container image, using technologies such as [Docker](https://www.docker.com) or [Container](https://github.com/apple/container), to deploy to your service onto a virtual machine or to cloud hosting infrastructure, such as a Kubernetes cluster. + + The container includes the executable for your service, any runtime dependencies it needs, resources the service requires, a manifest to define configuration through environment variables, expose ports to your service, and a default command to run when the container is invoked. It combines these together into a portable image format that you run on servers. @@ -17,18 +23,17 @@ container: - ENV vars preset, or added by gow you run it - config value pattern - filesystems that your environment might provide (configmap) - container as layers of filesysten chnages, overlaid - creating read only layers that get loaded into a container runtime to executee your Swift swrvice in its own, isolated environment. your organization may provide vetted basr images, reviewed for your organizations best practices, security considerations, and so on. build on these base images to provide images that you can deploy, both locally to test and to run in production. -simple build -dual build to slim -cache intermediate build steps to optimize build times -run locally to vet or debug -push to a registry to makr iy available fot other systems to pull it - + +### simple build +### dual build to slim +### cache intermediate build steps to optimize build times +### push to a registry to make it available for other systems +### run locally to vet or debug ## Docker From ab9da65a136e1b620d62e7b7767a5aa7249c0cff Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 9 Mar 2026 16:26:23 -0700 Subject: [PATCH 05/12] swift images, ref to org base images --- .../Sources/ServerGuides.docc/packaging.md | 84 ++++++++++++------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index d4409e1..583d918 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -7,28 +7,59 @@ Build a container that includes your Swift service with its resources and depend -When you run your service in the cloud, it requires configuration, dependencies, and potentially resources to run correctly. -A common means of packaging your app and what it needs are containers. -Create an OCI container image, using technologies such as [Docker](https://www.docker.com) or [Container](https://github.com/apple/container), to deploy to your service onto a virtual machine or to cloud hosting infrastructure, such as a Kubernetes cluster. +Running your service in the cloud requires configuration, dependencies, and resources. +Containers provide a standard way to package your service along with everything it needs. +You build a container image using tools such as [Docker](https://www.docker.com) or [Container](https://github.com/apple/container), +then deploy that image to a virtual machine or cloud hosting infrastructure +such as a Kubernetes cluster. -The container includes the executable for your service, any runtime dependencies it needs, resources the service requires, a manifest to define configuration through environment variables, expose ports to your service, and a default command to run when the container is invoked. -It combines these together into a portable image format that you run on servers. +A container image packages everything your service needs to run into a single, portable artifact. +The image's filesystem contains: -container: -- linux dependencies -- local filesysten with resources -- command to invokr by defauly when you "run" the container -- port that is exposed to provide access to incoming connections for listening sockets -- ENV vars preset, or added by gow you run it - config value pattern -- filesystems that your environment might provide (configmap) +- The compiled executable for your service +- Linux system libraries and runtime dependencies +- Resources your service requires, such as configuration files or static assets -container as layers of filesysten chnages, overlaid - creating read only layers that get loaded into a container runtime to executee your Swift swrvice in its own, isolated environment. +The image also declares runtime metadata that controls how a container starts: -your organization may provide vetted basr images, reviewed for your organizations best practices, security considerations, and so on. build on these base images to provide images that you can deploy, both locally to test and to run in production. +- A default command to run +- Ports to expose for incoming network connections +- Default values for environment variables that configure your service + +A container image is built from layers of filesystem changes, stacked on top of each other. +Each layer is read-only and shared across images that use it, which makes images efficient to store and transfer. +When you run an image, the container runtime adds a writable layer on top and starts your service as a container: +a running instance of the image in its own isolated environment. + +At runtime, your deployment environment can mount additional filesystems +into a container, such as configuration maps or secrets, to provide values that vary between environments. +### Swift container images + +The Swift project publishes container images on [Docker Hub](https://hub.docker.com/_/swift) that you can use to build and run your services. +These images come in two forms: + +- **Full SDK images** include the Swift compiler, standard library, and development tools. + For example, `swift:6.2-noble` provides Swift 6.2 on Ubuntu 24.04. + +- **Slim runtime images** include only the Swift runtime libraries on a minimal Linux distribution. + Use these as the final stage in a multi-step image build to keep your deployed image small. + For example, `swift:6.2-noble-slim` provides a minimal Ubuntu 24.04 image with the Swift runtime. + +You can also use a plain Linux distribution image, such as `ubuntu:noble` or `ubuntu:noble-slim`, as your base image when you statically link the Swift standard library. + + +The Swift Docker images are available for Ubuntu, Debian, Amazon Linux, Fedora, and Red Hat UBI. + +> Tip: Pin your images to a specific Swift version, such as `swift:6.2-noble`, rather than using the `latest` tag. +> Pinning the image makes your builds more reproducible and prevents unexpected changes when a new Swift release is published. + +Your organization may provide vetted base images that reflect its security policies and operational best practices. +If it does, build on those base images. + ### simple build ### dual build to slim ### cache intermediate build steps to optimize build times @@ -40,14 +71,12 @@ your organization may provide vetted basr images, reviewed for your organization Using Docker's tooling, we can build and package the application as a Docker image, publish it to a Docker repository, and later launch it directly on a server or on a platform that supports Docker deployments such as [Kubernetes](https://kubernetes.io). Many public cloud providers including AWS, GCP, Azure, IBM and others encourage this kind of deployment. -Here is an example `Dockerfile` that builds and packages the application on top of CentOS: +Here is an example `Dockerfile` that builds and packages the application using Ubuntu: ```Dockerfile #------- build ------- -FROM swift:centos8 as builder +FROM swift:6.2-noble AS builder -# set up the workspace -RUN mkdir /workspace WORKDIR /workspace # copy the source to the docker image @@ -56,7 +85,7 @@ COPY . /workspace RUN swift build -c release --static-swift-stdlib #------- package ------- -FROM centos +FROM ubuntu:noble # copy executables COPY --from=builder /workspace/.build/release/ / @@ -91,15 +120,12 @@ See [Docker's documentation](https://docs.docker.com/engine/reference/commandlin [Distroless](https://github.com/GoogleContainerTools/distroless) is a project by Google that attempts to create minimal images containing only the application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution. -Since distroless supports Docker and is based on Debian, packaging a Swift application on it is fairly similar to the Docker process above. Here is an example `Dockerfile` that builds and packages the application on top of a distroless's C++ base image: +Since distroless supports Docker and is based on Debian, packaging a Swift application on it is fairly similar to the Docker process above. Here is an example `Dockerfile` that builds and packages the application on top of distroless's C++ base image: ```Dockerfile #------- build ------- -# Building using Ubuntu Bionic since its compatible with Debian runtime -FROM swift:bionic as builder +FROM swift:6.2-noble AS builder -# set up the workspace -RUN mkdir /workspace WORKDIR /workspace # copy the source to the docker image @@ -110,7 +136,7 @@ RUN swift build -c release --static-swift-stdlib #------- package ------- # Running on distroless C++ since it includes # all(*) the runtime dependencies Swift programs need -FROM gcr.io/distroless/cc-debian10 +FROM gcr.io/distroless/cc-debian12 # copy executables COPY --from=builder /workspace/.build/release/ / @@ -118,7 +144,7 @@ COPY --from=builder /workspace/.build/release/ / CMD [""] ``` -Note the above uses `gcr.io/distroless/cc-debian10` as the runtime image which should work for Swift programs that do not use `FoundationNetworking` or `FoundationXML`. In order to provide more complete support we (the community) could put in a PR into distroless to introduce a base image for Swift that includes `libcurl` and `libxml` which are required for `FoundationNetworking` and `FoundationXML` respectively. +Note the above uses `gcr.io/distroless/cc-debian12` as the runtime image which should work for Swift programs that do not use `FoundationNetworking` or `FoundationXML`. In order to provide more complete support we (the community) could put in a PR into distroless to introduce a base image for Swift that includes `libcurl` and `libxml` which are required for `FoundationNetworking` and `FoundationXML` respectively. ## Archive (Tarball, ZIP file, etc.) @@ -134,7 +160,7 @@ First, use the `docker run` command from the application's source location to bu $ docker run --rm \ -v "$PWD:/workspace" \ -w /workspace \ - swift:bionic \ + swift:6.2-noble \ /bin/bash -cl "swift build -c release --static-swift-stdlib" ``` @@ -146,7 +172,7 @@ Next we can create a staging area with the application's executable: $ docker run --rm \ -v "$PWD:/workspace" \ -w /workspace \ - swift:bionic \ + swift:6.2-noble \ /bin/bash -cl ' \ rm -rf .build/install && mkdir -p .build/install && \ cp -P .build/release/ .build/install/' @@ -164,7 +190,7 @@ We can test the integrity of the tarball by extracting it to a directory and run ```bash $ cd -$ docker run -v "$PWD:/app" -w /app bionic ./ +$ docker run -v "$PWD:/app" -w /app ubuntu:noble ./ ``` Deploying the application's tarball to the target server can be done using utilities like `scp`, or in a more sophisticated setup using configuration management system like `chef`, `puppet`, `ansible`, etc. From 664d13f76e9398076d2f025ffc2ef95a565027d2 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 9 Mar 2026 17:10:42 -0700 Subject: [PATCH 06/12] more sections --- .../Sources/ServerGuides.docc/packaging.md | 160 +++++++++++++++++- 1 file changed, 156 insertions(+), 4 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index 583d918..91b3f42 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -60,10 +60,162 @@ The Swift Docker images are available for Ubuntu, Debian, Amazon Linux, Fedora, Your organization may provide vetted base images that reflect its security policies and operational best practices. If it does, build on those base images. -### simple build -### dual build to slim -### cache intermediate build steps to optimize build times -### push to a registry to make it available for other systems +### Build a container image + +A single-stage build compiles and packages your service in one image. +Create a file named `Dockerfile` at the root of your project: + +```Dockerfile +FROM swift:6.2-noble + +WORKDIR /workspace +COPY . /workspace + +RUN swift build -c release --static-swift-stdlib + +CMD [".build/release/"] +``` + +Build the image, using the `-t` flag tags the image with a name and version: + +```bash +container build -t :latest . +``` + +Tags identify images locally and determine where images go when you push them to a registry. +The convention is `:` — for example, `my-app:1.0` or `my-app:latest`. +Choose a name that matches your service and a version that reflects the build. +If you don't include `:` and a version string, the tools default to `:latest`. + +> Note: The examples in this guide use the `container` command from [Apple's container tool](https://github.com/apple/container). +> The `docker` command accepts the same arguments and works the same way. + +This approach works, and is handy for a quick local build for testing locally. +However, the image includes the full Swift compiler and development tools that +adds hundreds of megabytes your service never uses at runtime +and increases the attack surface of the deployed container. + +### Slim the image with a multi-stage build + +A [multi-stage build](https://docs.docker.com/build/building/multi-stage/) separates compilation from packaging. +The first stage compiles your service using the full SDK image. +The second stage copies only the compiled executable into a minimal runtime image, +producing a final image that is a fraction of the size. + +```Dockerfile +# Stage 1: Build +FROM swift:6.2-noble AS builder + +WORKDIR /workspace +COPY . /workspace + +RUN swift build -c release --static-swift-stdlib + +# Stage 2: Package +FROM ubuntu:noble-slim +COPY --from=builder /workspace/.build/release/ / + +CMD ["/"] +``` + +The `--static-swift-stdlib` flag links the Swift standard library into your executable, +so the final image does not need the Swift runtime installed. +If your service uses `FoundationNetworking` or `FoundationXML`, use the image `swift:6.2-noble-slim` for your runtime based image, instead of `ubuntu:noble-slim`. +This image includes system libraries that those frameworks use (`libcurl` and `libxml2`). + +Build and run the image the same way: + +```bash +container build -t :latest . +``` + +### Cache build artifacts to speed up rebuilds + +The Dockerfiles above copy all source files into the image, and build from scratch every time. +For a small project this is fine, but as your service grows, +rebuilding all dependencies on every change slows down the development cycle. + +[Docker BuildKit cache mounts](https://docs.docker.com/build/cache/) +persist directories across builds so that Swift Package Manager's resolved packages +and compiled artifacts survive between invocations. +Combined with bind mounts that pass in only the files each step needs, +this avoids copying files into the image layer and keeps the cache effective +even when source files change. + +The following Dockerfile resolves dependencies and builds in separate steps, +each with a cache mount for the build directory: + +```Dockerfile +# Stage 1: Build +FROM swift:6.2-noble AS builder + +WORKDIR /workspace + +# Resolve dependencies — re-runs only when Package.swift or Package.resolved change +RUN --mount=type=cache,target=/tmp/.build,sharing=locked \ + --mount=type=bind,source=Package.swift,target=Package.swift \ + --mount=type=bind,source=Package.resolved,target=Package.resolved \ + swift package resolve --force-resolved-versions --build-path /tmp/.build + +# Build the executable — reuses cached dependencies and prior compilation results +RUN --mount=type=cache,target=/tmp/.build,sharing=locked \ + --mount=type=bind,source=Package.swift,target=Package.swift \ + --mount=type=bind,source=Package.resolved,target=Package.resolved \ + --mount=type=bind,source=./Sources,target=./Sources \ + swift build -c release --static-swift-stdlib \ + --product --build-path /tmp/.build && \ + cp /tmp/.build/release/ /usr/local/bin/ + +# Stage 2: Package +FROM ubuntu:noble-slim +COPY --from=builder /usr/local/bin/ / + +CMD ["/"] +``` + +Cache mounts (`--mount=type=cache`) keep the `/tmp/.build` directory across builds +so Swift Package Manager reuses previously resolved packages and compiled modules. +Bind mounts (`--mount=type=bind`) pass specific files into each `RUN` step +without adding them to the image layer, +which means changes to your source files don't invalidate the cache. + +Because cache mounts aren't part of the final image layer, +the build step explicitly copies the compiled binary to a known path +before the second stage picks it up. + +> Note: If your project includes a `Snippets/` directory or other non-source directories +> referenced by `Package.swift`, add additional bind mounts for those directories +> in the build step. + +### Push to a registry + +A container registry stores and distributes images so that other systems can pull and run them. +To push an image, tag it with the registry's hostname and repository path, +then push the tagged image. + +Using [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) as an example: + +```bash +container tag :latest ghcr.io//:latest +container push ghcr.io//:latest +``` + +Pushing requires authentication with the registry. +See [Docker's registry authentication documentation](https://docs.docker.com/reference/cli/docker/login/) +for how to log in from the command line. + +> Tip: If you're using `container` instead of `docker`, then use the command `container registry login` to authenticate a registry. + +You can also apply the full registry tag directly during the build: + +```bash +container build -t ghcr.io//:1.0 . +container push ghcr.io//:1.0 +``` + +> Note: Your organization or cloud hosting provider may operate its own container registry. +> Check with your team for the correct registry hostname and authentication method. + ### run locally to vet or debug From 116848783e2594acea71c1191588c0082d8c55e2 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 9 Mar 2026 17:20:47 -0700 Subject: [PATCH 07/12] more packaging --- .../Sources/ServerGuides.docc/packaging.md | 85 ++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index 91b3f42..34aa59b 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -73,9 +73,14 @@ COPY . /workspace RUN swift build -c release --static-swift-stdlib +EXPOSE 8080 CMD [".build/release/"] ``` +The `EXPOSE` directive declares which port your service listens on. +It doesn't publish the port — you do that at runtime with `-p` — but it documents +the intended network interface and is used by some orchestrators and tooling. + Build the image, using the `-t` flag tags the image with a name and version: ```bash @@ -129,6 +134,11 @@ Build and run the image the same way: container build -t :latest . ``` +> Tip: Create a `.dockerignore` file at the root of your project to exclude directories +> like `.build/` and `.git/` from the build context. +> Without it, `COPY . /workspace` sends everything to the build daemon, +> which slows builds and can bloat image layers. + ### Cache build artifacts to speed up rebuilds The Dockerfiles above copy all source files into the image, and build from scratch every time. @@ -187,6 +197,39 @@ before the second stage picks it up. > referenced by `Package.swift`, add additional bind mounts for those directories > in the build step. +### Build for a different platform + +If your development machine and deployment target use different CPU architectures — +for example, building on Apple Silicon (arm64) and deploying to x86_64 cloud infrastructure — +you need to specify the target platform when building the image. + +With `docker`, use the `--platform` flag: + +```bash +docker build --platform linux/amd64 -t :latest . +``` + +To build for multiple architectures in a single command, +use `docker buildx` which produces a multi-architecture image manifest: + +```bash +docker buildx build --platform linux/amd64,linux/arm64 -t :latest . +``` + +See [Docker's multi-platform build documentation](https://docs.docker.com/build/building/multi-platform/) +for setup details, including creating a builder that supports cross-platform emulation. + +Apple's `container` tool builds for the host architecture by default. +To target a different architecture, use the `--arch` flag: + +```bash +container build --arch amd64 -t :latest . +``` + +> Note: Cross-platform builds use emulation, which is significantly slower than native builds. +> When possible, build on infrastructure that matches your deployment architecture, +> such as an x86_64 CI runner for x86_64 deployments. + ### Push to a registry A container registry stores and distributes images so that other systems can pull and run them. @@ -216,7 +259,47 @@ container push ghcr.io//:1.0 > Note: Your organization or cloud hosting provider may operate its own container registry. > Check with your team for the correct registry hostname and authentication method. -### run locally to vet or debug +### Run locally to verify and debug + +Run a container from your image to verify it starts correctly: + +```bash +container run :latest +``` + +To expose a port from the container to your host machine, use the `-p` flag. +The first number is the host port, the second is the container port: + +```bash +container run -p 8080:8080 :latest +``` + +Pass environment variables with `-e` to configure your service without rebuilding: + +```bash +container run -p 8080:8080 -e LOG_LEVEL=debug :latest +``` + +Mount a local directory into the container with `-v` to provide configuration files +or inspect output your service writes to disk: + +```bash +container run -v "$PWD/config:/config" :latest +``` + +For interactive debugging, combine `--rm` and `-it`. +The `--rm` flag removes the container when it exits so stopped containers don't accumulate. +The `-it` flags attach an interactive terminal, +which lets you interrupt the process with Control-C +or explore the container's filesystem if you override the default command: + +```bash +container run --rm -it :latest /bin/sh +``` + +This drops you into a shell inside the container where you can inspect +the filesystem, check that files are in the expected locations, +and verify the runtime environment. ## Docker From 107641f9e5946c951f259f595a7b5f8680d5e1de Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 9 Mar 2026 18:44:01 -0700 Subject: [PATCH 08/12] grammar and style fixes --- .../deploying-static-binaries.md | 186 ++++++++++++++++++ .../Sources/ServerGuides.docc/packaging.md | 169 ++-------------- 2 files changed, 198 insertions(+), 157 deletions(-) create mode 100644 server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md diff --git a/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md b/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md new file mode 100644 index 0000000..dbed94d --- /dev/null +++ b/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md @@ -0,0 +1,186 @@ +# Deploying Swift services as static binaries + +Build fully self-contained Swift executables and deploy them +to minimal containers or directly to Linux machines. + +## Overview + +The Static Linux SDK produces Swift executables with no external dependencies — +not even the C library. +This lets you run the binary on any Linux machine regardless of distribution, +which opens deployment options that aren't possible with dynamically linked builds. + +You can copy the binary into a `scratch` or distroless container image +that contains nothing but your executable, +or skip containers entirely and copy the binary to a virtual machine. +Either way, the result is the smallest possible deployment artifact +with the smallest possible attack surface. + +This article walks you through this process: +- install the SDK +- build a static binary +- deploy it + +### Install the Static Linux SDK + +The Static Linux SDK requires an open-source Swift toolchain from [swift.org](https://www.swift.org/install/). +The toolchain bundled with Xcode doesn't support SDK-based cross-compilation. + +Install the SDK with `swift sdk install`, providing the URL for your Swift version. + +Check the [Swift Install page](https://www.swift.org/install/) for the Swift SDK for Static Linux under SDK bundles +for the current install command. +The command follows this form: + +```bash +swift sdk install --checksum +``` + +After installation, verify the SDK is available: + +```bash +swift sdk list +``` + +The output includes SDK names like `x86_64-swift-linux-musl` +and `aarch64-swift-linux-musl` that you use in build commands. + +### Build a static binary + +Build your service with the `--swift-sdk` flag, +specifying the target architecture: + +```bash +swift build -c release --swift-sdk aarch64-swift-linux-musl +``` + +For x86_64 targets, use the corresponding SDK name: + +```bash +swift build -c release --swift-sdk x86_64-swift-linux-musl +``` + +This produces a statically linked ELF binary in `.build/release/` +that you can copy to any Linux system and run directly. +The SDK uses [Musl](https://musl.libc.org) instead of Glibc, +and statically links everything the executable needs — +including the Swift standard library, Foundation, and system libraries +like libcurl and libxml2. + +> Note: Because Musl replaces Glibc, +> some packages that import C libraries need changes. +> For some, the fix is changing import lines from `Glibc` to `Musl`. +> The Swift standard library and Foundation handle this automatically. + +### Deploy to a distroless container image + +A distroless container image contains no operating system utilities — +no package manager, no shell, nothing but what you put in it. +This eliminates an entire class of potential vulnerabilities +because there are no extra programs an attacker could exploit. + +Because the Static Linux SDK cross-compiles from your development machine, +you don't need a multi-stage Dockerfile with a Swift SDK builder image. +Build the binary locally, then use a minimal `Dockerfile` +that copies the pre-built executable into a `scratch` image — +a completely empty base image: + +```bash +swift build -c release --swift-sdk aarch64-swift-linux-musl +``` + +```Dockerfile +FROM scratch +COPY .build/release/ / + +EXPOSE 8080 +ENTRYPOINT ["/"] +``` + +The final image contains a single file: your executable. + +If your service makes outbound TLS connections, +the image also needs CA certificates. +Copy them from any Linux image at build time: + +```Dockerfile +FROM ubuntu:noble AS certs +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates + +FROM scratch +COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY .build/release/ / + +EXPOSE 8080 +ENTRYPOINT ["/"] +``` + +Build the container image: + +```bash +container build -t :latest . +``` + +> Note: A `scratch`-based image doesn't include a shell, +> so you can't use `container run --rm -it /bin/sh` to debug. +> If you need a shell for troubleshooting, +> use a slim base image during development and switch to `scratch` for production. + +### Build and publish with Swift Container Plugin + +The [Swift Container Plugin](https://github.com/apple/swift-container-plugin) +combines the build, packaging, and registry push into a single command. +It builds your service with the Static Linux SDK, +packages the executable into a container image, +and publishes the image to a container registry — all without a Dockerfile. + +Add the plugin to your `Package.swift`: + +```swift +dependencies: [ + .package(url: "https://github.com/apple/swift-container-plugin", from: "0.1.0"), +], +``` + +Then build and publish: + +```bash +swift package --swift-sdk aarch64-swift-linux-musl \ + build-container-image --repository registry.example.com/my-app +``` + +The plugin produces a minimal image equivalent to a `scratch`-based Dockerfile +and pushes it to the specified registry. +Run the published image with any container runtime: + +```bash +container run -p 8080:8080 registry.example.com/my-app +``` + +> Tip: The container plugin is especially useful in CI pipelines +> where you want a single, repeatable command +> that produces a ready-to-deploy image. + +### Deploy directly to a Linux machine + +Containers aren't required. +A static binary runs on any Linux machine, +so you can copy it directly to a virtual machine or bare-metal server: + +```bash +scp .build/release/ user@server:/usr/local/bin/ +``` + +On the server, run the executable directly: + +```bash +ssh user@server /usr/local/bin/ +``` + +No Swift runtime, no system libraries, and no container runtime needed. +Use your preferred configuration management tool — +Ansible, Chef, Puppet, or a simple shell script — +to automate deployment across multiple hosts. + +> Tip: Pair direct deployment with a systemd service unit +> to manage your service's lifecycle, restart policy, and logging. diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index 34aa59b..eec3d77 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -35,8 +35,6 @@ a running instance of the image in its own isolated environment. At runtime, your deployment environment can mount additional filesystems into a container, such as configuration maps or secrets, to provide values that vary between environments. - - ### Swift container images The Swift project publishes container images on [Docker Hub](https://hub.docker.com/_/swift) that you can use to build and run your services. @@ -49,7 +47,7 @@ These images come in two forms: Use these as the final stage in a multi-step image build to keep your deployed image small. For example, `swift:6.2-noble-slim` provides a minimal Ubuntu 24.04 image with the Swift runtime. -You can also use a plain Linux distribution image, such as `ubuntu:noble` or `ubuntu:noble-slim`, as your base image when you statically link the Swift standard library. +You can also use a plain Linux distribution image, such as `ubuntu:noble`, as your base image when you statically link the Swift standard library. The Swift Docker images are available for Ubuntu, Debian, Amazon Linux, Fedora, and Red Hat UBI. @@ -81,7 +79,8 @@ The `EXPOSE` directive declares which port your service listens on. It doesn't publish the port — you do that at runtime with `-p` — but it documents the intended network interface and is used by some orchestrators and tooling. -Build the image, using the `-t` flag tags the image with a name and version: +Build the image. +The `-t` flag tags the image with a name and version: ```bash container build -t :latest . @@ -95,9 +94,9 @@ If you don't include `:` and a version string, the tools default to `:latest`. > Note: The examples in this guide use the `container` command from [Apple's container tool](https://github.com/apple/container). > The `docker` command accepts the same arguments and works the same way. -This approach works, and is handy for a quick local build for testing locally. -However, the image includes the full Swift compiler and development tools that -adds hundreds of megabytes your service never uses at runtime +This approach works for a quick local build, +but the image includes the full Swift compiler and development tools, +which add hundreds of megabytes your service never uses at runtime and increases the attack surface of the deployed container. ### Slim the image with a multi-stage build @@ -117,15 +116,16 @@ COPY . /workspace RUN swift build -c release --static-swift-stdlib # Stage 2: Package -FROM ubuntu:noble-slim +FROM ubuntu:noble COPY --from=builder /workspace/.build/release/ / +EXPOSE 8080 CMD ["/"] ``` The `--static-swift-stdlib` flag links the Swift standard library into your executable, so the final image does not need the Swift runtime installed. -If your service uses `FoundationNetworking` or `FoundationXML`, use the image `swift:6.2-noble-slim` for your runtime based image, instead of `ubuntu:noble-slim`. +If your service uses `FoundationNetworking` or `FoundationXML`, use the image `swift:6.2-noble-slim` for your runtime-based image, instead of `ubuntu:noble`. This image includes system libraries that those frameworks use (`libcurl` and `libxml2`). Build and run the image the same way: @@ -177,9 +177,10 @@ RUN --mount=type=cache,target=/tmp/.build,sharing=locked \ cp /tmp/.build/release/ /usr/local/bin/ # Stage 2: Package -FROM ubuntu:noble-slim +FROM ubuntu:noble COPY --from=builder /usr/local/bin/ / +EXPOSE 8080 CMD ["/"] ``` @@ -247,7 +248,7 @@ Pushing requires authentication with the registry. See [Docker's registry authentication documentation](https://docs.docker.com/reference/cli/docker/login/) for how to log in from the command line. -> Tip: If you're using `container` instead of `docker`, then use the command `container registry login` to authenticate a registry. +> Tip: If you're using `container` instead of `docker`, then use the command `container registry login` to authenticate with a registry. You can also apply the full registry tag directly during the build: @@ -300,149 +301,3 @@ container run --rm -it :latest /bin/sh This drops you into a shell inside the container where you can inspect the filesystem, check that files are in the expected locations, and verify the runtime environment. - - -## Docker - -Using Docker's tooling, we can build and package the application as a Docker image, publish it to a Docker repository, and later launch it directly on a server or on a platform that supports Docker deployments such as [Kubernetes](https://kubernetes.io). Many public cloud providers including AWS, GCP, Azure, IBM and others encourage this kind of deployment. - -Here is an example `Dockerfile` that builds and packages the application using Ubuntu: - -```Dockerfile -#------- build ------- -FROM swift:6.2-noble AS builder - -WORKDIR /workspace - -# copy the source to the docker image -COPY . /workspace - -RUN swift build -c release --static-swift-stdlib - -#------- package ------- -FROM ubuntu:noble -# copy executables -COPY --from=builder /workspace/.build/release/ / - -# set the entry point (application name) -CMD [""] -``` - -To create a local Docker image from the `Dockerfile` use the `docker build` command from the application's source location, e.g.: - -```bash -$ docker build . -t : -``` - -To test the local image use the `docker run` command, e.g.: - -```bash -$ docker run : -``` - -Finally, use the `docker push` command to publish the application's Docker image to a Docker repository of your choice, e.g.: - -```bash -$ docker tag : /: -$ docker push /: -``` - -At this point, the application's Docker image is ready to be deployed to the server hosts (which need to run docker), or to one of the platforms that supports Docker deployments. - -See [Docker's documentation](https://docs.docker.com/engine/reference/commandline/) for more complete information about Docker. - -### Distroless - -[Distroless](https://github.com/GoogleContainerTools/distroless) is a project by Google that attempts to create minimal images containing only the application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution. - -Since distroless supports Docker and is based on Debian, packaging a Swift application on it is fairly similar to the Docker process above. Here is an example `Dockerfile` that builds and packages the application on top of distroless's C++ base image: - -```Dockerfile -#------- build ------- -FROM swift:6.2-noble AS builder - -WORKDIR /workspace - -# copy the source to the docker image -COPY . /workspace - -RUN swift build -c release --static-swift-stdlib - -#------- package ------- -# Running on distroless C++ since it includes -# all(*) the runtime dependencies Swift programs need -FROM gcr.io/distroless/cc-debian12 -# copy executables -COPY --from=builder /workspace/.build/release/ / - -# set the entry point (application name) -CMD [""] -``` - -Note the above uses `gcr.io/distroless/cc-debian12` as the runtime image which should work for Swift programs that do not use `FoundationNetworking` or `FoundationXML`. In order to provide more complete support we (the community) could put in a PR into distroless to introduce a base image for Swift that includes `libcurl` and `libxml` which are required for `FoundationNetworking` and `FoundationXML` respectively. - -## Archive (Tarball, ZIP file, etc.) - -Since cross-compiling Swift for Linux is not (yet) supported on Mac or Windows, we need to use virtualization technologies like Docker to compile applications we are targeting to run on Linux. - -That said, this does not mean we must also package the applications as Docker images in order to deploy them. While using Docker images for deployment is convenient and popular, an application can also be packaged using a simple and lightweight archive format like tarball or ZIP file, then uploaded to the server where it can be extracted and run. - -Here is an example of using Docker and `tar` to build and package the application for deployment on Ubuntu servers: - -First, use the `docker run` command from the application's source location to build it: - -```bash -$ docker run --rm \ - -v "$PWD:/workspace" \ - -w /workspace \ - swift:6.2-noble \ - /bin/bash -cl "swift build -c release --static-swift-stdlib" -``` - -Note we are bind mounting the source directory so that the build writes the build artifacts to the local drive from which we will package them later. - -Next we can create a staging area with the application's executable: - -```bash -$ docker run --rm \ - -v "$PWD:/workspace" \ - -w /workspace \ - swift:6.2-noble \ - /bin/bash -cl ' \ - rm -rf .build/install && mkdir -p .build/install && \ - cp -P .build/release/ .build/install/' -``` - -Note this command could be combined with the build command above--we separated them to make the example more readable. - -Finally, create a tarball from the staging directory: - -```bash -$ tar cvzf -.tar.gz -C .build/install . -``` - -We can test the integrity of the tarball by extracting it to a directory and running the application in a Docker runtime container: - -```bash -$ cd -$ docker run -v "$PWD:/app" -w /app ubuntu:noble ./ -``` - -Deploying the application's tarball to the target server can be done using utilities like `scp`, or in a more sophisticated setup using configuration management system like `chef`, `puppet`, `ansible`, etc. - - -## Source Distribution - -Another distribution technique popular with dynamic languages like Ruby or Javascript is distributing the source to the server, then compiling it on the server itself. - -To build Swift applications directly on the server, the server must have the correct Swift toolchain installed. [Swift.org](/download/#linux) publishes toolchains for a variety of Linux distributions, make sure to use the one matching your server Linux version and desired Swift version. - -The main advantage of this approach is that it is easy. Additional advantage is the server has the full toolchain (e.g. debugger) that can help troubleshoot issues "live" on the server. - -The main disadvantage of this approach that the server has the full toolchain (e.g. compiler) which means a sophisticated attacker can potentially find ways to execute code. They can also potentially gain access to the source code which might be sensitive. If the application code needs to be cloned from a private or protected repository, the server needs access to credentials which adds additional attack surface area. - -In most cases, source distribution is not advised due to these security concerns. - -## Static linking and Curl/XML - -**Note:** if you are compiling with `-static-stdlib` and using Curl with FoundationNetworking or XML with FoundationXML you must have libcurl and/or libxml2 installed on the target system for it to work. From cd4a08dd36fe724bf05a01ebf953b004a82d4b07 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 9 Mar 2026 18:53:08 -0700 Subject: [PATCH 09/12] more fixes --- .../deploying-static-binaries.md | 18 ++++++------- .../Sources/ServerGuides.docc/packaging.md | 26 +++++++------------ 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md b/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md index dbed94d..3bd3804 100644 --- a/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md +++ b/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md @@ -16,10 +16,8 @@ or skip containers entirely and copy the binary to a virtual machine. Either way, the result is the smallest possible deployment artifact with the smallest possible attack surface. -This article walks you through this process: -- install the SDK -- build a static binary -- deploy it +This article walks you through installing the SDK, +building a static binary, and deploying it. ### Install the Static Linux SDK @@ -28,8 +26,8 @@ The toolchain bundled with Xcode doesn't support SDK-based cross-compilation. Install the SDK with `swift sdk install`, providing the URL for your Swift version. -Check the [Swift Install page](https://www.swift.org/install/) for the Swift SDK for Static Linux under SDK bundles -for the current install command. +Check the [Swift Install page](https://www.swift.org/install/) for the current install command, +listed under SDK bundles as the Swift SDK for Static Linux. The command follows this form: ```bash @@ -62,14 +60,14 @@ swift build -c release --swift-sdk x86_64-swift-linux-musl This produces a statically linked ELF binary in `.build/release/` that you can copy to any Linux system and run directly. -The SDK uses [Musl](https://musl.libc.org) instead of Glibc, +The SDK uses [Musl](https://musl.libc.org) instead of Glibc and statically links everything the executable needs — including the Swift standard library, Foundation, and system libraries like libcurl and libxml2. > Note: Because Musl replaces Glibc, > some packages that import C libraries need changes. -> For some, the fix is changing import lines from `Glibc` to `Musl`. +> For some packages, the fix is changing import lines from `Glibc` to `Musl`. > The Swift standard library and Foundation handle this automatically. ### Deploy to a distroless container image @@ -129,7 +127,7 @@ container build -t :latest . ### Build and publish with Swift Container Plugin The [Swift Container Plugin](https://github.com/apple/swift-container-plugin) -combines the build, packaging, and registry push into a single command. +combines building, packaging, and pushing to a registry into a single command. It builds your service with the Static Linux SDK, packages the executable into a container image, and publishes the image to a container registry — all without a Dockerfile. @@ -149,7 +147,7 @@ swift package --swift-sdk aarch64-swift-linux-musl \ build-container-image --repository registry.example.com/my-app ``` -The plugin produces a minimal image equivalent to a `scratch`-based Dockerfile +The plugin produces a minimal image equivalent to one from a `scratch`-based Dockerfile and pushes it to the specified registry. Run the published image with any container runtime: diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index eec3d77..2c5f543 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -4,16 +4,12 @@ Build a container that includes your Swift service with its resources and depend ## Overview - - - Running your service in the cloud requires configuration, dependencies, and resources. Containers provide a standard way to package your service along with everything it needs. You build a container image using tools such as [Docker](https://www.docker.com) or [Container](https://github.com/apple/container), then deploy that image to a virtual machine or cloud hosting infrastructure such as a Kubernetes cluster. - A container image packages everything your service needs to run into a single, portable artifact. The image's filesystem contains: @@ -44,14 +40,11 @@ These images come in two forms: For example, `swift:6.2-noble` provides Swift 6.2 on Ubuntu 24.04. - **Slim runtime images** include only the Swift runtime libraries on a minimal Linux distribution. - Use these as the final stage in a multi-step image build to keep your deployed image small. + Use these as the final stage in a multi-stage build to keep your deployed image small. For example, `swift:6.2-noble-slim` provides a minimal Ubuntu 24.04 image with the Swift runtime. You can also use a plain Linux distribution image, such as `ubuntu:noble`, as your base image when you statically link the Swift standard library. - -The Swift Docker images are available for Ubuntu, Debian, Amazon Linux, Fedora, and Red Hat UBI. - > Tip: Pin your images to a specific Swift version, such as `swift:6.2-noble`, rather than using the `latest` tag. > Pinning the image makes your builds more reproducible and prevents unexpected changes when a new Swift release is published. @@ -76,8 +69,7 @@ CMD [".build/release/"] ``` The `EXPOSE` directive declares which port your service listens on. -It doesn't publish the port — you do that at runtime with `-p` — but it documents -the intended network interface and is used by some orchestrators and tooling. +It doesn't publish the port — you do that at runtime with `-p` — but it documents the intended network interface, and some orchestrators and tools use it. Build the image. The `-t` flag tags the image with a name and version: @@ -96,8 +88,8 @@ If you don't include `:` and a version string, the tools default to `:latest`. This approach works for a quick local build, but the image includes the full Swift compiler and development tools, -which add hundreds of megabytes your service never uses at runtime -and increases the attack surface of the deployed container. +which add hundreds of megabytes that your service never uses at runtime +and expand the attack surface of the deployed container. ### Slim the image with a multi-stage build @@ -124,7 +116,7 @@ CMD ["/"] ``` The `--static-swift-stdlib` flag links the Swift standard library into your executable, -so the final image does not need the Swift runtime installed. +so the final image doesn't need the Swift runtime installed. If your service uses `FoundationNetworking` or `FoundationXML`, use the image `swift:6.2-noble-slim` for your runtime-based image, instead of `ubuntu:noble`. This image includes system libraries that those frameworks use (`libcurl` and `libxml2`). @@ -141,8 +133,8 @@ container build -t :latest . ### Cache build artifacts to speed up rebuilds -The Dockerfiles above copy all source files into the image, and build from scratch every time. -For a small project this is fine, but as your service grows, +The Dockerfiles above copy all source files into the image and build from scratch every time. +For a small project, this is fine, but as your service grows, rebuilding all dependencies on every change slows down the development cycle. [Docker BuildKit cache mounts](https://docs.docker.com/build/cache/) @@ -211,7 +203,7 @@ docker build --platform linux/amd64 -t :latest . ``` To build for multiple architectures in a single command, -use `docker buildx` which produces a multi-architecture image manifest: +use `docker buildx`, which produces a multi-architecture image manifest: ```bash docker buildx build --platform linux/amd64,linux/arm64 -t :latest . @@ -248,7 +240,7 @@ Pushing requires authentication with the registry. See [Docker's registry authentication documentation](https://docs.docker.com/reference/cli/docker/login/) for how to log in from the command line. -> Tip: If you're using `container` instead of `docker`, then use the command `container registry login` to authenticate with a registry. +> Tip: If you're using `container` instead of `docker`, authenticate with `container registry login`. You can also apply the full registry tag directly during the build: From 25c7a98e5ffff08d45d3d513dd03ee4187d45cfa Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 9 Mar 2026 18:56:34 -0700 Subject: [PATCH 10/12] split up some of the longer sentences --- .../Sources/ServerGuides.docc/packaging.md | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index 2c5f543..bdcd403 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -138,11 +138,11 @@ For a small project, this is fine, but as your service grows, rebuilding all dependencies on every change slows down the development cycle. [Docker BuildKit cache mounts](https://docs.docker.com/build/cache/) -persist directories across builds so that Swift Package Manager's resolved packages -and compiled artifacts survive between invocations. -Combined with bind mounts that pass in only the files each step needs, -this avoids copying files into the image layer and keeps the cache effective -even when source files change. +persist directories across builds. +Swift Package Manager's resolved packages and compiled artifacts survive between invocations. +Bind mounts pass in only the files each step needs +without copying them into the image layer, +which keeps the cache effective even when source files change. The following Dockerfile resolves dependencies and builds in separate steps, each with a cache mount for the build directory: @@ -176,11 +176,11 @@ EXPOSE 8080 CMD ["/"] ``` -Cache mounts (`--mount=type=cache`) keep the `/tmp/.build` directory across builds -so Swift Package Manager reuses previously resolved packages and compiled modules. +Cache mounts (`--mount=type=cache`) keep the `/tmp/.build` directory across builds. +Swift Package Manager reuses previously resolved packages and compiled modules. Bind mounts (`--mount=type=bind`) pass specific files into each `RUN` step -without adding them to the image layer, -which means changes to your source files don't invalidate the cache. +without adding them to the image layer. +Changes to your source files don't invalidate the cache. Because cache mounts aren't part of the final image layer, the build step explicitly copies the compiled binary to a known path @@ -281,15 +281,13 @@ container run -v "$PWD/config:/config" :latest ``` For interactive debugging, combine `--rm` and `-it`. -The `--rm` flag removes the container when it exits so stopped containers don't accumulate. -The `-it` flags attach an interactive terminal, -which lets you interrupt the process with Control-C -or explore the container's filesystem if you override the default command: +The `--rm` flag removes the container when it exits, so stopped containers don't accumulate. +The `-it` flags attach an interactive terminal. +You can interrupt the process with Control-C, or override the default command to explore the container's filesystem: ```bash container run --rm -it :latest /bin/sh ``` -This drops you into a shell inside the container where you can inspect -the filesystem, check that files are in the expected locations, -and verify the runtime environment. +This drops you into a shell inside the container. +You can inspect the filesystem, check that files are in the expected locations, and verify the runtime environment. From c8dba69d30688c07788a438807f9db9af99cbe80 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Tue, 10 Mar 2026 08:09:29 -0700 Subject: [PATCH 11/12] pre-fixes --- .../ServerGuides.docc/Documentation.md | 3 +- .../deploying-static-binaries.md | 29 ++++++------------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/Documentation.md b/server-guides/Sources/ServerGuides.docc/Documentation.md index 8f21cb3..2b997d7 100644 --- a/server-guides/Sources/ServerGuides.docc/Documentation.md +++ b/server-guides/Sources/ServerGuides.docc/Documentation.md @@ -7,4 +7,5 @@ ## Topics - - +- +- diff --git a/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md b/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md index 3bd3804..86b1f08 100644 --- a/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md +++ b/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md @@ -10,24 +10,20 @@ not even the C library. This lets you run the binary on any Linux machine regardless of distribution, which opens deployment options that aren't possible with dynamically linked builds. -You can copy the binary into a `scratch` or distroless container image -that contains nothing but your executable, -or skip containers entirely and copy the binary to a virtual machine. -Either way, the result is the smallest possible deployment artifact -with the smallest possible attack surface. +You can copy the binary into a `scratch` or distroless container image that contains nothing but your executable. +Or skip containers entirely and copy the binary to a virtual machine. +Either way, the result is the smallest possible deployment artifact with the smallest possible attack surface. -This article walks you through installing the SDK, -building a static binary, and deploying it. +This article walks you through installing the SDK, building a static binary, and deploying it. ### Install the Static Linux SDK -The Static Linux SDK requires an open-source Swift toolchain from [swift.org](https://www.swift.org/install/). +The Static Linux SDK requires an open source Swift toolchain. The toolchain bundled with Xcode doesn't support SDK-based cross-compilation. +Use swiftly to install an open source Swift toolchain, then install the static SDK for Linux for that toolchain. +Go to [swift.org](https://www.swift.org/install/) for the links and commands to install both. Install the SDK with `swift sdk install`, providing the URL for your Swift version. - -Check the [Swift Install page](https://www.swift.org/install/) for the current install command, -listed under SDK bundles as the Swift SDK for Static Linux. The command follows this form: ```bash @@ -161,9 +157,7 @@ container run -p 8080:8080 registry.example.com/my-app ### Deploy directly to a Linux machine -Containers aren't required. -A static binary runs on any Linux machine, -so you can copy it directly to a virtual machine or bare-metal server: +A static binary runs on any Linux machine, so you can copy it directly to a virtual machine or bare-metal server: ```bash scp .build/release/ user@server:/usr/local/bin/ @@ -175,10 +169,5 @@ On the server, run the executable directly: ssh user@server /usr/local/bin/ ``` -No Swift runtime, no system libraries, and no container runtime needed. -Use your preferred configuration management tool — -Ansible, Chef, Puppet, or a simple shell script — +Use your preferred configuration management tool, such as Ansible, Chef, Puppet, or shell scripts, to automate deployment across multiple hosts. - -> Tip: Pair direct deployment with a systemd service unit -> to manage your service's lifecycle, restart policy, and logging. From da0ba4c56085cdcbc01871602426b8e1180f4d4b Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Tue, 10 Mar 2026 08:34:22 -0700 Subject: [PATCH 12/12] more grammar fixes, tightening wording --- .../Sources/ServerGuides.docc/deploying-static-binaries.md | 6 +++--- server-guides/Sources/ServerGuides.docc/packaging.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md b/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md index 86b1f08..58dd832 100644 --- a/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md +++ b/server-guides/Sources/ServerGuides.docc/deploying-static-binaries.md @@ -20,7 +20,7 @@ This article walks you through installing the SDK, building a static binary, and The Static Linux SDK requires an open source Swift toolchain. The toolchain bundled with Xcode doesn't support SDK-based cross-compilation. -Use swiftly to install an open source Swift toolchain, then install the static SDK for Linux for that toolchain. +Use `swiftly` to install an open source Swift toolchain, then install the Static Linux SDK for that toolchain. Go to [swift.org](https://www.swift.org/install/) for the links and commands to install both. Install the SDK with `swift sdk install`, providing the URL for your Swift version. @@ -62,8 +62,8 @@ including the Swift standard library, Foundation, and system libraries like libcurl and libxml2. > Note: Because Musl replaces Glibc, -> some packages that import C libraries need changes. -> For some packages, the fix is changing import lines from `Glibc` to `Musl`. +> some packages that import C libraries need changes — +> for example, changing import lines from `Glibc` to `Musl`. > The Swift standard library and Foundation handle this automatically. ### Deploy to a distroless container image diff --git a/server-guides/Sources/ServerGuides.docc/packaging.md b/server-guides/Sources/ServerGuides.docc/packaging.md index bdcd403..de223ff 100644 --- a/server-guides/Sources/ServerGuides.docc/packaging.md +++ b/server-guides/Sources/ServerGuides.docc/packaging.md @@ -117,8 +117,8 @@ CMD ["/"] The `--static-swift-stdlib` flag links the Swift standard library into your executable, so the final image doesn't need the Swift runtime installed. -If your service uses `FoundationNetworking` or `FoundationXML`, use the image `swift:6.2-noble-slim` for your runtime-based image, instead of `ubuntu:noble`. -This image includes system libraries that those frameworks use (`libcurl` and `libxml2`). +If your service uses `FoundationNetworking` or `FoundationXML`, use `swift:6.2-noble-slim` instead of `ubuntu:noble` for the runtime image. +This image includes system libraries that those frameworks require: `libcurl` and `libxml2`. Build and run the image the same way: