Swift Infrastructure as Code
The fastest way to build and deploy server side Swift applications.
Swift Cloud is based on the premise that infrastructure should be defined along side your application, in the same language as your application. In our case, Swift. Define a new target, describe your infrastructure, and deploy it with a single command. There's no Dockerfiles, no Terrafrom configurations, no Node.js packages. Everything is defined in Swift and the complex configuration is handled behind the scenes, using modern architecture best practices.
let jobHandler = AWS.Function(
"job-handler",
targetName: "JobProcessor",
memory: 512,
timeout: .seconds(10)
)
let queue = AWS.Queue("job-queue")
queue.subscribe(jobHandler)
let server = AWS.WebServer(
"hummingbird-server",
targetName: "App",
concurrency: 1,
autoScaling: .init(
maximumConcurrency: 10,
metrics: [.cpu(50), .memory(50)]
)
)
server.link(queue)
swift run Infra deploy --stage production
The Swift Cloud package is powered by Pulumi.
Specifically, the SDK vends Swift components that are compiled into Pulumi YAML
files, and then the Pulumi CLI is used to deploy your application. You do not
need a Pulumi account to use Swift Cloud, nor do you need to install Pulumi CLI
on your machine. Everything is managed by the SDK and written to a .cloud
directory in your project.
In order to use Swift Cloud you need to have Docker installed on your machine. This is a short term limitation until Swift 6 where we will be able to natively cross-compile to Linux and other SDKs.
If you're on a Mac the easiest way to install Docker is OrbStack. Simply download OrbStack and run the installer.
You will need to have an AWS account and AWS credentials loaded on your machine or in the typical environment variables.
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
You can also provide a .env
file in the root of your project with the same
relevant variables.
If you're on a Mac the easiest way to manage your AWS credentials is Leapp.
You can also use the AWS CLI to configure your credentials:
aws configure
dependencies: [
.package(url: "https://github.com/swift-cloud/swift-cloud.git", branch: "main")
]
Swift Cloud works by declaring your infrastructure as Swift code. To do this you must create a new executable target in the same package as your application.
Start by defining a new executable target in your Package.swift
file:
targets: [
...
.executableTarget(
name: "Infra",
dependencies: [
.product(name: "Cloud", package: "swift-cloud")
]
)
]
Next, inside your Sources
directory create a new folder called Infra
.
Finally, add a new Swift file called Project.swift
:
import Cloud
@main
struct SwiftCloudDemo: AWSProject {
func build() async throws -> Outputs {
let server = AWS.WebServer(
"my-vapor-web-server",
targetName: "App",
concurrency: 1,
autoScaling: .init(
maximumConcurrency: 10,
metrics: [.cpu(50), .memory(50)]
)
)
return Outputs([
"url": server.url,
])
}
}
swift run Infra deploy --stage production
Swift Cloud is invoked directly from your Swift package. You can run the following commands:
Deploy your infrastructure:
swift run Infra deploy --stage production
Remove all resources:
swift run Infra remove --stage development
Preview changes before deploying:
swift run Infra preview --stage development
Show the outputs of your deployment:
swift run Infra outputs --stage development
Cancel a deployment:
swift run Infra cancel --stage development
Swift Cloud allows you to deploy infrastructure across multiple cloud providers. In order to handle incremental changes to your infrastructure, Swift Cloud must store your underlying configuration in a durable location so it can be referenced anytime you run a deploy, whether from your local machine or a CI/CD pipeline.
We abstracted this concept into a HomeProvider
protocol, and allow you to
decide where your configuration is stored. By default, Swift Cloud uses the AWS
S3 service to store your configuration, but you can easily swap this out for any
other provider that supports the HomeProvider
protocol.
For quick prototyping, you can use the Home.Local
provider, which stores your
configuration in a local file. This is great for testing and development, but
it's not recommended for production use.
import Cloud
@main
struct SwiftCloudDemo: AWSProject {
// Override the default home provider with a local provider
let home = Home.Local()
func build() async throws -> Outputs {...}
}
This component creates a high performance web server using an application load balancer, auto-scaling group, and Fargate. Everything is fully managed and scales automatically based on your configuration.
let server = AWS.WebServer(
"my-vapor-web-server",
targetName: "App",
concurrency: 1,
autoScaling: .init(
maximumConcurrency: 10,
metrics: [.cpu(50), .memory(50)]
)
)
let lambda = AWS.Function(
"my-lambda-function",
targetName: "App",
url: .enabled(cors: true),
memory: 512,
timeout: .seconds(10)
)
This component creates a CDN that sits in front of your application. It can be used to cache your application assets, or to serve your application from a custom domain.
let cdn = AWS.CDN(
"my-cdn",
origins: .webServer(server)
)
You can also route traffic on different paths to different resources:
let cdn = AWS.CDN(
"my-cdn",
origins: [
.function(function, path: "/api/*"),
.webServer(server, path: "*")
]
)
And of course you can use a custom domain:
let cdn = AWS.CDN(
"my-cdn",
origins: .function(function),
domainName: .init("www.example.com")
)
let bucket = AWS.Bucket("my-s3-bucket")
let queue = AWS.Queue("my-sqs-queue")
// Subscribe a lambda function to the queue to process messages
queue.subscribe(
AWS.Function("my-lambda-function", targetName: "App")
)
let table = AWS.DynamoDB(
"MyTable",
primaryIndex: .init(
partitionKey: ("type", .string),
sortKey: ("id", .string)
)
)
// Link the table to a function or web server
function.link(table)
let vpc = AWS.VPC("my-vpc")
let cache = AWS.Cache(
"my-valkey-cache",
engine: .valkey(), // or .redis() or .memcached()
vpc: .private(vpc)
)
// Allow the function or web server to connect to the cache
function.link(cache)
let database = AWS.SQLDatabase(
"my-postgres-database",
engine: .postgres(),
vpc: .private(vpc)
)
// Allow the function or web server to connect to the database
function.link(database)
let topic = AWS.Topic("my-sns-topic")
// Subscribe a lambda function to the topic to process events
topic.subscribe(
AWS.Function("my-lambda-function", targetName: "App")
)
let cron = AWS.Cron(
"my-cron-job",
schedule: .rate(.minutes(5))
)
// Invoke the function when the cron job runs
cron.invoke(
AWS.Function("my-lambda-function", targetName: "App")
)
The DomainName
construct manages a TLS certificate and the necessary
validation, and can be linked to a WebServer
to provide a fully managed domain
name.
Important: We support 3 providers for domains:
AWS
,Cloudflare
, andVercel
.
let domainName = DomainName(
hostname: "www.example.com",
dns: .aws(zoneName: "example.com")
)
let server = AWS.WebServer(
"my-vapor-web-server",
targetName: "App",
domainName: domainName
)
return Outputs([
// Now server url will be `https://www.example.com`
"url": server.url
])
You can link resources together to provide the necessary permissions to access each other. This is more secure than sharing access key ids and secrets in environment variables.
For example you can link an S3 bucket to a Lambda function:
myFunction.link(bucket)
This allows the lambda function to access the bucket without needing to share access keys.
You can use linked resources in your server or function via environment variables in your application:
let bucketUrl = ProcessInfo.processInfo.environment["BUCKET_MEDIA_URL"]
Here is a list of all the linked resources:
Resource | Environment Variable |
---|---|
AWS S3 Bucket | BUCKET_<NAME>_URL |
AWS S3 Bucket | BUCKET_<NAME>_HOSTNAME |
AWS S3 Bucket | BUCKET_<NAME>_NAME |
AWS SQS Queue | QUEUE_<NAME>_URL |
AWS SQS Queue | QUEUE_<NAME>_NAME |
AWS Lambda Function | FUNCTION_<NAME>_URL |
AWS Lambda Function | FUNCTION_<NAME>_NAME |
AWS DynamoDB Table | DYNAMODB_<NAME>_NAME |
AWS Cache | CACHE_<NAME>_HOSTNAME |
AWS Cache | CACHE_<NAME>_PORT |
AWS Cache | CACHE_<NAME>_URL |
AWS SQL Database | SQLDB_<NAME>_HOSTNAME |
AWS SQL Database | SQLDB_<NAME>_PORT |
AWS SQL Database | SQLDB_<NAME>_DATABASE_NAME |
AWS SQL Database | SQLDB_<NAME>_USERNAME |
AWS SQL Database | SQLDB_<NAME>_PASSWORD |
AWS SQL Database | SQLDB_<NAME>_URL |