How to deploy Swift to Digital Ocean App Platform
Recent developments in Swift greatly simplified and improved the deployment story on Linux. You can now build minimum containers optimized for deployment on cloud platforms.
While it is straight-forward to write scalable backends with Swift on Server and Vapor, the deployment story has been quite underwhelming. The only way that worked well in the past was to setup a Virtual Machine with Linux, copy the source over and build the app using the Linux toolchain in case of changes.
I actually tried multiple different approaches to build static binaries for linux in the past, as this had been already announced for Swift 5.3. Apparently it took them some time to get it right. Recently, deployment got much easier. Creating a fully static executable and cross-compilation now works great.
In my opinion, the most straight-forward approach to deploy an app now is cross-compilation of a static executable on MacOS with Linux as target, wrapping the executable in an very simple container and deploying the image to your registry of choice.
Step 1: Preparations
-
Install the latest Swift toolchain from the offical site using the package installer.
-
Install the latest Swift Linux SDK (for example for 6.0.1) using instructions here.
-
Install lima via Homebrew or MacPorts.
Step 2: Create a new vapor project
Create a new Vapor project using the command line:
vapor new app
Use the installed swift toolchain and compile the executable:
cd app
xcrun --toolchain swift swift build --swift-sdk swift-6.0.1-RELEASE_static-linux-0.0.1
Check that the file actually is actually compatible with the target architecture:
file .build/x86_64-swift-linux-musl/debug/App
Step 3: Create a container with lima
Create a new Dockerfile:
FROM scratch
COPY ./App ./App
# Let Docker bind to port 8080
EXPOSE 8080
ENTRYPOINT ["./App"]
CMD ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"]
Use lima to create a new container:
limactl start --vm-type=vz --rosetta
lima nerdctl build --platform=amd64 -t registry.digitalocean.com/matt-do/api:latest --file Dockerfile .
lima nerdctl login registry.digitalocean.com
lima nerdctl push registry.digitalocean.com/matt-do/api
The result is absolut tiny container image, compiled without installing an additional Swift toolchain for Linux. While the build will still require you to install lima on the local machine and the only other option would be to assemble a container using some kind of magic, it is currently my absolute preferred option for optimizing build time in the best possible way.