Publish a Swift static website on GitLab Pages
6 min read

Publish a Swift static website on GitLab Pages

How to host your static website for free. Generate the static pages using Swift Publish and host on GitLab Pages.

One of the reasons why static websites are so hot right now is hosting prices.

There are many ways to host a static website for free, you can use the free tier of Netlify, GitHub Pages, Firebase Hosting, Amazon S3, and many others. If you already use GitLab to host the code of your projects, you can use GitLab Pages.

In this article, we will set up a new static website generated by Swift Publish and hosted by GitLab.

Create a repository on GitLab

The name and location you use to create your repository that will contain your website source code will dictate the URL of the website in GitLab Pages.

On if you create the project in your user space and name it using your username followed by, like in [YOUR_USERNAME], then it will be published on https://[YOUR_USERNAME] If you name it anything else, like in [ANYTHING_ELSE] it will be published on


If you create it in a group and name it [GROUP_NAME], then the website will be available at https://[GROUP_NAME]

On a self-hosted GitLab instance, the URL depends on the instance settings. GitLab pages could even be disabled on a self-hosted instance. You should contact the instance administrator if you are in doubt.

In this article, we will create a project named DeliciousRecipes on my user space, so it will be available on

Go to (or to your self hosted GitLab instance) and log in with your account. Click on New Project, type "DeliciousRecipes" as project name, leave the project slug as it is, and add a description. You can create a private project if you want, the visibility of the website does not depend on the visibility of the project.

Finally, click Create Project.

Init a new Swift Publish website

To create a new website, we follow the official quick start guide.

Download and build tools

Open Terminal and run

git clone
cd Publish

As described in the official guide, you might need to install the Xcode command-line tools:

xcode-select --install

Init your website source code

Create a new folder and run publish new in it:

mkdir DeliciousRecipes
cd DeliciousRecipes
publish new

Now you can run publish run in the same folder, and the project will build, fetching all the needed dependencies. When it's done, the website should be available at http://localhost:8000. If not, check the logs from the publish run command, it could be available on a different port.

Add GitLab CI configuration

GitLab Pages use GitLab CI/CD to (possibly) build your static website and deployit. We could just use it to deploy the website, but we also want to build it using GitLab CI/CD, because, then, we do not need to keep the compiled HTML and CSS in the repository.

We start by creating a new file, called .gitlab-ci.yml, in the root of our project:

touch .gitlab-ci.yml
open .gitlab-ci.yml

The content of the file should be:

# (1)
image: swift:5.2.4

# (2)
  - swift build

    # (3)
    - swift run
    # (4)
    - mv Output public
      # (5)
      - public
    # (6)
    - master

where we instruct GitLab CI/CD to (1) use the Swift Docker container with Swift 5.2.4, which is the latest at the time of writing, then to (2) build the project so that all the dependencies are downloaded.

After the project is built, we (3) run it, so that the static website pages are generated, and we (4) move the assets from the Output directory to the publicdirectory, which will be used by GitLab as the directory containing the assets to deploy.

In (5) we define the path to the artifact generated by GitLab CI/CD. It looks like this cannot be changed to a custom folder.

Finally, we also (6) specify that the website should be published only when we push to the master branch so that we can use other branches for work in progresson the website, for new pages or articles.

If you use the SassPublishPlugin or the MagickPublishPlugin, you can change the Docker image to ishouldgotosleep/swift-publish:latest, which is described in another article, and it includes the tools to run those plugins.

# (1)
image: ishouldgotosleep/swift-publish:latest

Push the repository

Finally, we link the repository in GitLab and what we have been creating locally on our machine. Go to your project page:[YOUR_GITLAB_USERNAME]/deliciousrecipes and follow the instructions in Push an existing folder:

git init
git remote add origin [email protected]:[YOUR_GITLAB_USERNAME]/deliciousrecipes.git
git add .
git commit -m "Initial commit"
git push -u origin master

Now if you reload the page you will see that a pipeline is running:

and if you click on the small blue pipeline indicator, you can see that it is a GitLab Pages job.

Click on the pages job indicator to get to the detail page of the job, where you can see what is happening in the Docker container that is generating the website:

In the picture above, the worker that picked up the pages job is downloading the Docker container that we specified in the configuration file.

After a couple of minutes the job should be done:

Check the result

Go to https://[YOUR_GITLAB_USERNAME] and you are greeted with a webpage without style:

This happens because the default theme does not support (yet) a website hosted in a subfolder. We need to use a different theme. For instance CasperishTheme, by Arnaud Joubay.

Use a different theme

We just need to add the theme to our dependencies in Package.swift

// swift-tools-version:5.1

import PackageDescription

let package = Package(
  name: "DeliciousRecipes",
  products: [
    .executable(name: "DeliciousRecipes", targets: ["DeliciousRecipes"])
  dependencies: [
    .package(url: "", from: "0.3.0"),
    .package(url: "", .branch("master")) // <-- added this
  targets: [
      name: "DeliciousRecipes",
      dependencies: ["Publish", "CasperishTheme"] // <-- changed this

and use the example they provide in their GitHub repository in our main.swift:

import Foundation
import Publish
import Plot
import CasperishTheme

// This type acts as the configuration for your website.
struct DeliciousRecipes: Website, CasperishWebsite {
    enum SectionID: String, WebsiteSectionID {
        // Add the sections that you want your website to contain here:
        case posts

    struct ItemMetadata: WebsiteItemMetadata, CasperishWebsiteItemMetadata {
        // Add any site-specific metadata that you want to use here.
        var cover: String?

    // Update these properties to configure your website:
    var url = URL(string: "")!
    var name = "MyWebsite"
    var description = "A description of MyWebsite"
    var language: Language { .english }
    var imagePath: Path? { nil }

    // Update these properties to configure your casperish-website:
    var rootPathString = "deliciousrecipes"
    var headerColor = "#424242"
    var cover = ""
    var author = "Your name"
    var avatar = ""
    var bio = "Bio"
    var icon = "🏝"
    var newsletterAction = ""
    var contacts: [(ContactPoint, String)] { [] }

// This will generate your website using the Casperish theme:
try DeliciousRecipes().publish(
    withTheme: .casperish,
    additionalSteps: [
    plugins: [.pygments()]

Push and visit again https://[YOUR_GITLAB_USERNAME] and now a nice styled website should appear.

Ignore Output folder

Now that we build the website in the Docker container via GitLab CI, we can delete the Output folder from the project and ignore it in .gitignore:



This is a link to the repository used in this article, while at this link you can find the final published website.

The enhanced Docker image, that can use the SassPublishPlugin and the MagickPublishPlugin, can be found on Docker Hub.

Subscribe to a curated newsletter


See previous issues of the newsletter.