Building a simple blog with enterprise-scale tools is like using a Formula 1 car for grocery shopping. Excessive? Absolutely. Worth it for the learning experience? Every single line of code.

The Sledgehammer Approach to Blogging

Imagine this: You need to hang a small picture frame on your wall. The sensible person grabs a hammer and a nail. Me? I fire up the industrial pneumatic drill, don full safety gear, and create a mounting system that could support a Boeing 747.

That’s exactly what I did with this blog.

A few static HTML files on GitHub Pages would serve the same content to the same three people who read my posts. Instead, I built a full Swift web application with Vapor, containerized it, set up cross-compilation between macOS and Linux, and deployed it to AWS.

Why? Because learning is fun, overkill is my middle name, and having AI as my coding sidekick feels like wielding a superpower.

What We’re Actually Building

This blog processes Markdown files with YAML frontmatter, handles dynamic routing, and manages content relationships between posts and apps. Could it be static? Sure. But building it with Vapor taught me server-side Swift, container deployment, and cross-compilation - knowledge I wouldn’t get from a static site generator.

The Mobile Developer’s Web Journey

As an iOS developer, web technology felt like alien territory (apart from API calls). Server routing, containerization, cloud deployment – it’s a different universe from SwiftUI or Auto Layout constraints.

Here’s the truth: this entire website was agentic coded with Claude Code. Not just debugging help or architecture advice – the actual implementation.

The workflow became incredibly efficient: describe what you want, Claude proposes the solution, review and iterate. Need a new feature? “Add support for related posts between blog entries and apps.” Claude handles the routing, data models, and template updates.

The Makefile became the control center – simple commands like make deploy-remote that hide complex AWS operations behind readable targets. Even AWS credential management got automated into make ecr-netrc. Claude wrote these too.

Learning Vapor: From iOS to Web

Coming from iOS development, Vapor feels surprisingly familiar. Here’s what a basic route looks like compared to a SwiftUI view:

// Vapor route handler
func home(req: Request) async throws -> View {
    let posts = try BlogPostManager.loadBlogPosts(from: "Content/Blog")
    let context = HomeContext(posts: posts)
    return try await req.view.render("home", context)
}

// SwiftUI equivalent concept
struct HomeView: View {
    @State private var posts: [BlogPost] = []
    var body: some View {
        // UI code here
    }
}

The patterns translate well - data loading, context preparation, and view rendering. Vapor routes are essentially view controllers with async/await support.

Key Vapor Concepts for iOS Developers:

  • Routes: Like navigation - app.get("blog", ":slug") maps to WebsiteController.blogPost

  • Middleware: Interceptors that process requests (think method swizzling but cleaner)

  • Request/Response: Similar to URLSession but server-side

  • Templates: Server-side equivalent of SwiftUI views using Leaf syntax

Here’s the actual routing setup:

func routes(_ app: Application) throws {
    let websiteController = WebsiteController()

    app.get(use: websiteController.home)
    app.get("blog", ":slug", use: websiteController.blogPost)
    app.get("apps", use: websiteController.apps)
    app.get("about", use: websiteController.about)
}

Clean, declarative, and type-safe - exactly what you’d expect from Swift.

The Cross-Compilation Dance

Here’s where the fun begins. I develop on a MacBook (ARM64), but AWS runs on x86_64 Linux. Swift’s cross-compilation capabilities let me build both architectures locally.

First, you need the right tools installed. You’ll need Swiftly for toolchain management and the Static Linux SDK for cross-compilation:

Install Swift 6.2 toolchain using swiftly:

swiftly install 6.2.0
swiftly use 6.2.0 --global-default

Install the Static Linux SDK for cross-compilation:

swift sdk install https://download.swift.org/swift-6.2-release/static-sdk/swift-6.2-RELEASE/swift-6.2-RELEASE_static-linux-0.0.1.artifactbundle.tar.gz --checksum d2225840e592389ca517bbf71652f7003dbf45ac35d1e57d98b9250368769378

Then add the Swift Container Plugin to your Package.swift as a dependency:

.package(url: "https://github.com/apple/swift-container-plugin", from: "1.4.5")

Now the actual build commands become simple.

Local development (ARM64 for my MacBook):

swift package --swift-sdk aarch64-swift-linux-musl --allow-network-connections all build-container-image --repository <local-registry>

Production deployment (x86_64 for AWS):

swift package --swift-sdk x86_64-swift-linux-musl --allow-network-connections all build-container-image --repository <AWS-ECR-registry>

The Swift Container Plugin handles the heavy lifting. It packages your Swift app, creates the container image, and pushes it to whatever registry you specify.

AWS App Runner vs. Alternatives:

  • App Runner: Zero infrastructure management, automatic scaling from containers

  • EC2: Too much server management for a personal blog

  • ECS: More complex setup than App Runner’s value justifies

  • Lambda: Swift cold starts are still painful

If you want to understand how all this works under the hood, check out this excellent talk explaining Swift Container Plugin, containers, and static Linux compilation:

The Local Development Setup

I run everything through Podman and a local registry. This lets me test the exact same container that’ll run in production.

Test locally before pushing to AWS:

podman run -p 8080:8080 --tls-verify=false <local-registry>:latest

No surprises. No “works on my machine” moments. If it runs locally, it runs in AWS.

The AWS Production Pipeline

The production setup is beautifully simple:

  1. Local Build: Cross-compile to x86_64 Linux using Swift Container Plugin

  2. ECR Push: Upload container to Elastic Container Registry

  3. App Runner Deploy: AWS automatically detects the new image and deploys

App Runner costs about $0.10/day, or roughly $3/month - reasonable for a personal blog hosting solution.

Swift Container Plugin requires AWS credentials in your ~/.netrc file for ECR authentication. The plugin documentation covers the setup details, but my Makefile handles the credential refresh automatically.

Zero CI/CD servers. Zero remote build machines. Everything happens on my MacBook.

The Learning Goldmine

This setup taught me:

  • Server-Side Swift: How Vapor compares to iOS development patterns

  • Container Deployment: Cross-compilation, registries, and production deployment

  • AWS Services: ECR, App Runner, and infrastructure automation

  • Development Workflow: Using AI for rapid iteration and learning

The learning wasn’t theoretical – every concept was immediately applicable to getting the blog working. That makes the difference between reading about containers and actually understanding them.

The Formula 1 Grocery Run

Yes, using Vapor for a personal blog is like taking a Formula 1 car to buy milk. It’s absurdly overpowered for the task. But here’s the thing – Formula 1 cars are engineering marvels. You learn more about automotive technology in one lap than you would in years of driving a Civic.

Building this site taught me more about web development, DevOps, and Swift’s server capabilities than any tutorial could. Every “overkill” decision was a learning opportunity disguised as engineering excess.

The Simple Complexity

The beautiful irony? Despite all this complexity, my deployment workflow is dead simple:

make deploy-remote

That’s it. One command cross-compiles, containerizes, and deploys to production. The complexity is hidden behind automation.

Worth Every Line

Could this be a static site? Absolutely.

Would it teach me about server-side Swift, containerization, cross-compilation, and cloud deployment? Not a chance.

Sometimes the journey matters more than the destination. This blog might be overkill by design, but it’s been the perfect vehicle for learning technologies I’d never touch in my day job.

And honestly? There’s something deeply satisfying about running swift build on your personal website. It feels like home.

Formula 1 car parked, groceries delivered. Next stop: probably over-engineering my todo list with microservices.