Homepage + Docker + Gitlab + Ansible
I was watching TechnoTim recently with his video about homepage. Homepage is a dashboard for all your home lab links. If you have used heimdall before, it is similar, but all the configuration is in yaml.
Since the config (Tim's write up has some exellent starting points) is in yaml, I created a simple repo in my local gitlab repo to help me control the configuration and back it up. This is quite straight forward and I'm not going to cover how to do that here.
Similarly, I set up another repo for my ansible playbook and started to look at deploying the container for homepage. Again, thanks to TechnoTim, I use Traefik on the front end of my docker containers to get TLS configured properly on each one. You should also configure ansible to able to log in to your host which is running the docker containers.
Much as TechnoTim seems to create folders for configs, I'm just going to use a standard volume for my deployment. This places in /var/lib/docker/volumes. It also gives the folder a _data subdirectory. Once you have updated the variables below, the playbook should create a volume, based on the containers name. I set the container as a variable, as I may well want to template this later on.
---
- name: "Homepage on Docker Playbook"
hosts: your.docker.host
become: yes
become_method: sudo
vars:
- container: homepage
- repo: 'https://gitlab.repo/userid/homepage-config.git'
- domain: 'your.domain'
vars_prompt:
- name: "gitlab_username"
prompt: "Enter your gitlab username"
private: no
- name: "gitlab_password"
prompt: "Enter your gitlab password"
private: yes
tasks:
- name: Create the homepage configuration volume
docker_volume:
name: "{{ container }}"
tags: volumecreate
Now that we have a volume that can be read by our container, we need to populate it. This is going to be done via git, and has a few hurdles. Namely, authentication. You could set the repo to public, but consider where it is and what you have stored. For this, I opted to be prompted for my gitlab creds (the vars_prompt part) and then cache them for the checkout.
- name: Add a setting to ~/.gitconfig
community.general.git_config:
name: safe.directory
scope: global
value: /var/lib/docker/volumes/{{ container }}/_data
- name: Configure Git credential storage
community.general.git_config:
name: credential.helper
scope: global
value: store
- name: Populate the Git credential store
template:
src: gitlab-creds-template.j2
dest: ~{{ ansible_user }}/.git-credentials
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: u=rw,g=,o=
no_log: true
- name: Populate the configuration directory
ansible.builtin.git:
repo: "{{ repo }}"
dest: /var/lib/docker/volumes/{{ container }}/_data
environment:
GIT_TERMINAL_PROMPT: 0
tags: gitpopulate
The template for the credentials looked like this:
If you need to debug anything so far, commenting out the no_log: true may help you in the short term, just don't forget to uncomment it later.
If everything is working up to this point, we need to deploy the container. This was quite straightforward to achieve, apart from the labels:
- name: Start Homepage and apply labels
docker_container:
name: Homepage
state: started
networks:
- name: proxy
image: ghcr.io/gethomepage/homepage:latest
env:
PUID: "1000"
PGID: "1000"
volumes:
- "{{ container }}:/app/config"
labels: "{{ my_labels }}"
tags: deployhomepage
At this stage, deplying the container through ansible will wipe out any config you may have set up previously, such as enviroment variables and labels.
I wanted to be clever with my labels, as I wanted to turn this playbook in to a template of sorts. That meant that the traefik labels needed to be dynamic. After much googling, I found this configuration:
- name: Create Traefik labels's dictionary
set_fact:
my_labels: "{{ my_labels | default({}) | combine ({ item.key : item.value }) }}"
with_items:
- { 'key': 'traefik.enable' , 'value': 'true'}
- { 'key': 'traefik.docker.network', 'value': "proxy"}
- { 'key': "traefik.http.middlewares.{{ container }}-https-redirect.redirectscheme.scheme", 'value': "https"}
- { 'key': "traefik.http.routers.{{ container }}-secure.entrypoints",'value': "https"}
- { 'key': "traefik.http.routers.{{ container }}-secure.rule",'value': "Host(`{{ container }}.{{ domain }}`)"}
- { 'key': "traefik.http.routers.{{ container }}-secure.service",'value': "{{ container }}"}
- { 'key': "traefik.http.routers.{{ container }}-secure.tls",'value': "true"}
- { 'key': "traefik.http.routers.{{ container }}.entrypoints",'value': "http"}
- { 'key': "traefik.http.routers.{{ container }}.middlewares",'value': "{{ container }}-https-redirect"}
- { 'key': "traefik.http.routers.{{ container }}.rule",'value': "Host(`{{ container }}.{{ domain }}`)"}
- { 'key': "traefik.http.services.{{ container }}.loadbalancer.server.port", 'value': "3000"}
Putting it all together, gives us a final playbook of:
---
- name: "Homepage on Docker Playbook"
hosts: your.docker.host
become: yes
become_method: sudo
vars:
- container: homepage
- repo: 'https://gitlab.repo/userid/homepage-config.git'
- domain: 'your.domain'
vars_prompt:
- name: "gitlab_username"
prompt: "Enter your gitlab username"
private: no
- name: "gitlab_password"
prompt: "Enter your gitlab password"
private: yes
tasks:
- name: Create the homepage configuration volume
docker_volume:
name: "{{ container }}"
tags: volumecreate
- name: Add a setting to ~/.gitconfig
community.general.git_config:
name: safe.directory
scope: global
value: /var/lib/docker/volumes/{{ container }}/_data
- name: Configure Git credential storage
community.general.git_config:
name: credential.helper
scope: global
value: store
- name: Populate the Git credential store
template:
src: gitlab-creds-template.j2
dest: ~{{ ansible_user }}/.git-credentials
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: u=rw,g=,o=
no_log: true
- name: Create Traefik labels's dictionary
set_fact:
my_labels: "{{ my_labels | default({}) | combine ({ item.key : item.value }) }}"
with_items:
- { 'key': 'traefik.enable' , 'value': 'true'}
- { 'key': 'traefik.docker.network', 'value': "proxy"}
- { 'key': "traefik.http.middlewares.{{ container }}-https-redirect.redirectscheme.scheme", 'value': "https"}
- { 'key': "traefik.http.routers.{{ container }}-secure.entrypoints",'value': "https"}
- { 'key': "traefik.http.routers.{{ container }}-secure.rule",'value': "Host(`{{ container }}.{{ domain }}`)"}
- { 'key': "traefik.http.routers.{{ container }}-secure.service",'value': "{{ container }}"}
- { 'key': "traefik.http.routers.{{ container }}-secure.tls",'value': "true"}
- { 'key': "traefik.http.routers.{{ container }}.entrypoints",'value': "http"}
- { 'key': "traefik.http.routers.{{ container }}.middlewares",'value': "{{ container }}-https-redirect"}
- { 'key': "traefik.http.routers.{{ container }}.rule",'value': "Host(`{{ container }}.{{ domain }}`)"}
- { 'key': "traefik.http.services.{{ container }}.loadbalancer.server.port", 'value': "3000"}
- name: Populate the configuration directory
ansible.builtin.git:
repo: "{{ repo }}"
dest: /var/lib/docker/volumes/{{ container }}/_data
environment:
GIT_TERMINAL_PROMPT: 0
tags: gitpopulate
- name: Start Homepage and apply labels
docker_container:
name: Homepage
state: started
networks:
- name: proxy
image: ghcr.io/gethomepage/homepage:latest
env:
PUID: "1000"
PGID: "1000"
volumes:
- "{{ container }}:/app/config"
labels: "{{ my_labels }}"
tags: deployhomepage
For future enhancements, I intend to deploy this via gitlab's CI/CD pipeline and a runner. The gitlab username and password can be specified via the command line.