# Deployer: Self-Hosted CI/CD for Swift Server Apps

**URL:** <https://forums.swift.org/t/deployer-self-hosted-ci-cd-for-swift-server-apps/86403>\
**Category:** Server\
**Created:** [May 1, 2026, 8:46am UTC](https://forums.swift.org/t/deployer-self-hosted-ci-cd-for-swift-server-apps/86403 "2026-05-01T08:46:16Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![mottzi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mottzi/32/40302_2.png) [@mottzi](https://forums.swift.org/u/mottzi)\
**Post date:** [May 1, 2026, 8:46am UTC](https://forums.swift.org/t/deployer-self-hosted-ci-cd-for-swift-server-apps/86403/1 "2026-05-01T08:46:16Z")

</div>

![rich text editor image](https://global.discourse-cdn.com/swift/original/3X/0/1/017753cbb1113f6340c1d6e0a9079532faff2ee6.jpeg)

GitHub repository: [click here](https://github.com/mottzi/Vapor-Deployer).

I built a self-hosted CI/CD tool specifically for Swift server apps powered by the Vapor framework. When you `git push` new code to your app's repository, Deployer intercepts this using webhooks. It then builds your app (`swift build`) and restarts it on your server.

A reactive live dashboard provides real time build output streams and deployment status changes, as well as start and stop buttons for your app and run buttons for the commits pushed on main. You can either use `manual` mode where deployments only start by pressing the run button in the panel, or `automatic` mode, where deployments happen instantly once new code is pushed. Only one build can run at a time; the last commit wins when multiple are pushed concurrently.

Most Swift server deploy setups I’ve tried ended up being a mix of shell scripts, GitHub Actions, and manual SSH steps. I wanted something simpler that allows for quick interation and production testing while being fully self-hosted. Deployer handles initial server setup through an automated and interactive CLI command, so it’s suitable even for developers that are new to server-side Swift.

**Tech Stack**

- **Framework:** [Vapor](https://github.com/vapor/vapor)

- **Database:** SQLite using [Fluent](https://github.com/vapor/fluent)

- **Live Dashboard:** [Mist](https://github.com/mottzi/Vapor-Mist)

**Setup**

To bootstrap on a fresh Ubuntu VPS, run (as root):

```swift
bash <(curl -sSL https://mottzi.codes/deployer/setup.sh)

```

1. Installs **Swift** (via Swiftly).

2. Configures **Nginx** reverse proxy

3. Issues **TLS certificates** (using Let's encrypt).

4. Sets up the **GitHub webhook** and **deploy key**.

5. Brings your app online.

**Control**

You manage Deployer via `deployerctl`, for example:

```swift
sudo deployerctl start # starts deployer and app 
sudo deployerctl status app
sudo deployerctl stop deployer
sudo deployerctl update # updates deployer itself

```

I'm interested in your thoughts and feedback. This release is pretty bear bones feature wise. It may have some rough edges, especially the front-end. You are very welcome to contribute, if you choose so.

---

<div class="post-metadata">

**Author:** ![VolodymyrBoichentsov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/volodymyrboichentsov/32/39767_2.png) [@VolodymyrBoichentsov](https://forums.swift.org/u/VolodymyrBoichentsov)\
**Post date:** [May 1, 2026, 12:09pm UTC](https://forums.swift.org/t/deployer-self-hosted-ci-cd-for-swift-server-apps/86403/2 "2026-05-01T12:09:24Z")

</div>

Is it mandatory to use sudo command?

---

<div class="post-metadata">

**Author:** ![mottzi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mottzi/32/40302_2.png) [@mottzi](https://forums.swift.org/u/mottzi)\
**Post date:** [May 1, 2026, 2:50pm UTC](https://forums.swift.org/t/deployer-self-hosted-ci-cd-for-swift-server-apps/86403/3 "2026-05-01T14:50:44Z")

</div>

Yes, sudo is currently enforced. You must use `sudo deployer setup` to install the environment, and `sudo deployerctl <cmd>` (update, restart, etc.) to manage it. Initial setup requires root because it installs packages, creates users, and configures system components like nginx and systemd.

That said, after setup the deployer and your app run as an unprivileged service user (e.g. `vapor`). Even when invoked via sudo, `deployerctl` immediately drops privileges and executes all runtime operations as that user.

Technically, the service user can manage its own services via `systemctl --user`, but `deployerctl` is currently designed as an operator-only control surface and enforces root. I think I'll revisit this and make non-root usage possible for commands where it’s technically feasible.

---

<div class="post-metadata">

**Author:** ![mottzi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mottzi/32/40302_2.png) [@mottzi](https://forums.swift.org/u/mottzi)\
**Post date:** [May 24, 2026, 6:40am UTC](https://forums.swift.org/t/deployer-self-hosted-ci-cd-for-swift-server-apps/86403/4 "2026-05-24T06:40:50Z")

</div>

I found some time to continue working on Deployer. Here is an update (v0.2.0):

The pitch is still generally the same: You `git push` your Swift app, Deployer catches the webhook, runs the pipeline, swaps the binary, restarts the service. A live dashboard streams the whole thing into your browser in real time (build output, status changes). No page refreshes required. One setup command on a fresh Ubuntu VPS and you're good to go.

What's new? You can now roll back to prior deployments: Every successful build gets archived, and clicking `Run`on a previous one swaps the binary back in instantly with no rebuild. This behaviour is configurable: you can keep the n newest builds, save n builds until a disk size limit is reached, keep everything, or keep nothing. This was the biggest piece of feedback from 0.1.

The whole pipeline now streams into the panel in real time. `git fetch`, `git checkout`, `swift test`, `swift build`, the binary swap, all of it. Before only the build step streamed its output to the clients. If a step fails, that step lights up red and the full transcript stays attached to the deployment so you can read it back later. I've added support for tests, which can be started automatically before every build, or manually by hitting the `Test`button on a deployment.

There's a settings page for editing your app's `.env` variables from the panel. One less thing you'd have to do via a terminal / SSH. Make an edit or add a new variable in the panel, click restart and the change is live. And Deployer can update itself now, from a button in the panel, with auto-rollback if the new version fails to boot. The old `deployerctl update` still works through the terminal.

The websocket panel does ping/pong heartbeats and per-component state restoration now so flaky connections doesn't desync the UI (or reconnecting clients, e.g. the laptop lid was closed, etc). If the server reboots or Deployer crashes mid-deployment, it, at next startup, picks up the most recent stranded push and resumes. And there's a new logo and a pretty significant amount of styling work on the panel itself.

 ![deployer-panel](https://global.discourse-cdn.com/swift/original/3X/d/e/de2b279111ebcebf8e6c8391ae9673906ccbcfa5.jpeg)
