I've recently been making an effort to migrate my server stack to use ansible to manage everything. So I wanted to see if I could compile and deploy my blog using just ansible. (spoiler: I can)
Turns out if you approach it the correct way, it's pretty easy. First I made a Dockerfile
which first installs cobalt,
then builds my site, and finally uses nginx to actually serve the static files. After that, I used ansible to clone my blog's repo
, build the docker image, and deploy it.
I broke my Dockerfile
into 2 sections. The first section (builder
) installs cobalt
and builds my blog.
FROM rust:1.73 AS builder
RUN cargo install cobalt-bin
COPY . /src
WORKDIR /src
RUN cobalt build
The second section copies the built files from the builder
section, and uses nginx
to serve the content.
FROM nginx
COPY --from=builder /src/_site /usr/share/nginx/html
In the end, I wound end with this dockerfile.
Now that I had an easy way to build a docker image, I just needed to automate that build process. Since I host my repos on GitLab I could've set up a seperate CI/CD pipeline that would push my docker image to GitLab's container registry. However as my docker image would also include all my static content, I don't really want to push the image to any registry if I can avoid it.
Since I'm already using ansible to deploy my existing services, I decided I should just try to use ansible. It ended up being simpler than expected.
Building the docker image basically just boils down to two steps. First, I clone my blog repo to get the content and Dockerfile
. Second, build the image.
Cloning my repo was very simple thanks to the ansible.builtin.git module.
- name: Pull blog repo
ansible.builtin.git:
dest: "your_dest_dir"
repo: "ssh://git@your.git/repo/url"
clone: true
accept_hostkey: true
single_branch: true
version: master
register: _blog
dest
sets where to clone the repo to. I chose to use a sub-directory of a directory where I store various config files for my other services, but you could also clone it into a temporary directoryrepo
is the URL to your git repo. I'd recommend using a ssh://
URL instead of https://
, especially if your repo is privateclone
just tells ansible that you want to clone the repo if there's not a repo at that dest
accept_hostkey
just tells ansible/SSH to be fine with whatever SSH hostkey the repo returns. This is useful if you're deploying to a new server that hasn't connected to your git provider before, but could potentially be dangeroussingle_branch
tells ansible to only clone one branch which saves time and bandwith versus downloading the whole repoversion
tells anisble which branch to cloneThen building the image was accomplished thanks to the community.docker.docker_image module.
- name: Build docker image
community.docker.docker_image:
build:
path: "your_dest_dir"
pull: true
name: blog
force_source: "{{ _blog.changed }}"
source: build
tag: latest
build.path
should be the same as the dest
option beforebuild.pull
tells ansible to pull the latest images for each image in your Dockerfile
. You can feel free to disable that thoughname
will be the name of the docker imagesource
forces ansible to build the image instead of pulling it or loading it from a local fileforce_source
is needed as otherwise ansible will only build the image if it doesn't existtag
sets the image tag. It defaults to latest
so you can omit that optionThen finally I deploy my blog as a normal docker container thanks to the community.docker.docker_container module.
- name: Deploy blog
community.docker.docker_container:
name: blog
image: blog:latest