A self-contained example of a static publishing platform for websites with Garage, Drone, StaticCMS and Gitea.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Quentin bf156c46ad last config 4 weeks ago
.gitignore wip 1 month ago
README.md last config 4 weeks ago
awsrc First version of the stack 1 month ago
docker-compose.yml last config 4 weeks ago
garage.toml many things 4 weeks ago
nginx.conf last config 4 weeks ago
teabag.env many things 4 weeks ago


Static Publishing Platform

created at: 2023-02-09. if you are reading this file years later, it is very probably obsolete.

based on:


Configure a CNAME wildcard pointing to your deployment machine. My wildcard is : *.vimaire.machine.dufour.io.

Launch the reverse proxy

docker-compose up -d reverse

Install Gitea

The recommended docker-compose is a good start for this toy example: https://docs.gitea.io/en-us/install-with-docker/

You need to configure CORS:


The last entry is required but ignored in Gitea version < 1.19, you must instead use a patched image to allow the custom header Content-Type. For example, I am using this one: superboum/gitea:1.17.4-cors.

Now you are ready to start your gitea instance:

docker-compose up -d gitea

Now go to http://git.vimaire.machine.dufour.io and configure your Gitea instance. Create an administrator account by unfolding the last section.

Install Teabag

The first step is to create some tokens for Teabag in Gitea. Go to this page: http://localhost:3000/user/settings/applications And create an Oauth2 application with the following parameters:

  • name: Teabag
  • callback: http://localhost:3001/callback

Now, use the example env file from the official repo as a template. Mine look like that:



This file is fetched from ./env/teabag.env or /etc/teabag/teabag.env, so pick one of this path.

Check that teabag is running:

$ curl -I http://localhost:3001
HTTP/1.1 200 OK
Date: Thu, 09 Feb 2023 13:32:15 GMT
Content-Length: 73
Content-Type: text/html; charset=utf-8

Create a blog with Jekyll

I don't really like CDNs so we will locally download the javasript file once and for all. For this example, we will create a basic jekyll blog.

nix-shell -p bundler
bundler install jekyll
bundle exec jekyll new my-blog
cd my-blog
bundle install
bundle exec jekyll serve

Now you website should be available at: http://localhost:4000.

You must now create a repository for your website on our target gitea instance. Mine is quentin/my-blog.

Use the gitea UI and then:

git init
git add .
git commit

git remote add origin http://localhost:3000/quentin/my-blog.git
git push -u origin main

Add "Static CMS" to your blog

In your blog folder (my-blog), create a file at this path admin/index.html with this content:

<!DOCTYPE html>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Content Manager</title>
    <!-- Include the identity widget -->
    <!-- Include the script that builds the page and powers Netlify CMS -->
    <script src="static-cms-app.js"></script>

And now, we must get the file static-cms-app.js. We will pull it from their CDN:

wget https://unpkg.com/@staticcms/app@^1.0.0/dist/static-cms-app.js


you can also build it:

git clone git@github.com:StaticJsCMS/static-cms.git
yarn build
cp packages/app/dist/static-cms-app.js .

and finally, we need a configuration file:

  name: gitea
  repo: quentin/my-blog
  base_url: http://localhost:3001
  api_root: http://localhost:3000/api/v1
  branch: main

media_folder: 'assets/'
site_url: http://my-blog.web.localhost
display_url: http://my-blog.web.localhost
locale: 'en'
  - name: 'article'
    label: 'Article'
    folder: '_posts/'
      preview: true
    create: true
    slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
      - { label: 'Layout', name: 'layout', widget: 'hidden', default: 'post' }
      - { label: 'Title', name: 'title', widget: 'string' }
      - { label: 'Publish Date', name: 'date', widget: 'datetime' }
      - { label: 'Body', name: 'body', widget: 'markdown' }

So, your blog folder should look like that (note the admin folder and its 3 files):

$ tree
├── 404.html
├── about.markdown
├── admin
│   ├── config.yml
│   ├── index.html
│   └── static-cms-app.js

And now you are ready to test your CMS admin interface!

bundle exec jekyll serve

And then go to: http://localhost:4000/admin/

Click on "Login with Gitea". Now you must see a dashboard, you should be able to create new articles too.

That's perfect, now you can commit your admin folder, you have a basic CMS integration.

git add admin
git commit -a
git pull && git push

Install Drone

Now we will want to automate the building of your website. We will need to create an Oauth2 client for Drone, similarly to Teabag. As in our example we are running Drone on localhost:3002, the callback URL is http://localhost:3002/login.

(fixme: mount the sqlite db on the host)

Once you have your credentials, you can launch the Drone daemon with the recommended parameters. Simply follow this doc: https://docs.drone.io/server/provider/gitea/

Then go to: http://localhost:3002
Authenticate yourself. And activate your blog repo (mine is quentin/my-blog).

You will also need a runner to execute your builds. Check this page: https://docs.drone.io/runner/overview/ I will use a docker runner on my side.

(note: I am currently using iptables -F as a dangerous workaround to docker's network firewalling)

Configure Drone on your repo

We will now create a .drone.yml file that will tell to Drone how to generate our static website.

Due to my strange deployment, I need a custom cloning logic. But this is not required in a normal deployment.

kind: pipeline
name: default

  disable: true

  - name: clone
    image: alpine/git
      - git clone .
      - git checkout $DRONE_COMMIT

  - name: build
    image: ruby
      - bundle install
      - bundle exec jekyll build

  - name: publish
    image: ruby
      - echo pretend we publish something

So, great, we know how to build our website! The last step is to publish it!

Install Garage

See: https://garagehq.deuxfleurs.fr/download/

docker pull dxflrs/garage:v0.8.1

We will use a simple config file you can generate as follow:

cat > garage.toml <<EOF
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "lmdb"

replication_mode = "none"

rpc_bind_addr = "[::]:3901"
rpc_public_addr = ""
rpc_secret = "$(openssl rand -hex 32)"

s3_region = "garage"
api_bind_addr = "[::]:3900"
root_domain = ".s3.localhost"

bind_addr = "[::]:3902"
root_domain = ".web.localhost"
index = "index.html"

api_bind_addr = "[::]:3904"

api_bind_addr = ""
admin_token = "$(openssl rand -base64 32)"

Configure the layout:

docker-compose exec garage /garage layout assign -z dc1 -c 1 559ee9e01f610572
docker-compose exec garage /garage layout apply --version 1

Configure a website on Garage

Create a key, a bucket and bind the key on it:

garage key new --name quentin
garage bucket create my-blog
garage bucket allow --owner --read --write --key quentin my-blog
garage key info quentin

Then put the key in a file awsrc:

export AWS_ACCESS_KEY_ID=GKdac05807405e200f8153595f
export AWS_SECRET_ACCESS_KEY=5c5710356eafc7d401fd3f5ecf5b50a5f0994f2e68d9fbaf678d91287abbd467
export AWS_DEFAULT_REGION='garage'

function aws { command aws --endpoint-url http://localhost:3900 $@ ; }
aws --version

Then we need to activate the website feature:

aws s3 website --index-document index.html s3://my-blog

Then test your website:

echo "hello world" > /tmp/index.html
aws s3 cp /tmp/index.html s3://my-blog/
curl -I http://my-blog.web.localhost:3902

Configure Drone to deploy your website on Garage

Add your keys as a drone secret in your repository. See here: https://docs.drone.io/secret/repository/

And then add this to your .drone.yml:

  - name: upload
    image: plugins/s3
      bucket: guide
      endpoint: http://s3.vimaire.machine.dufour.io
      region: garage
        from_secret: aws_access_key_id
        from_secret: aws_secret_acess_key
      source: public/**/*
      target: /
      strip_prefix: public/


Your blog should be available at this URL: http://guide.web.vimaire.machine.dufour.io You can administrate it at this URL: http://guide.web.vimaire.machine.dufour.io/admin/


  • Nettoyer les inter-connexions entre les services pour ne pas avoir à drop iptables -> passé en IPv6 avec un network dédié
  • Voir comment speed up le build
  • Peut-être passer la démo sur Hugo (aiderait le point précédent)
  • Tester avec des templates plus complexes (avec des images en bannière)
  • Porter ce test sur le guide Zola avec les nested collections -> Failed, trop buggy encore