Effortless Container Management on Unraid: Automating with Ansible and Docker

Unraid serves as an effective NAS operating system, allowing users to repurpose older hardware for network-attached storage. Integrating Docker enhances this capability by enabling network-attached computing through the execution of Docker containers. However, managing these containers solely via the built-in web console can become cumbersome, leading to inconsistencies in deployment. This guide will demonstrate how to leverage Ansible on your Unraid host for more efficient resource management and to implement Infrastructure as Code (IaC) principles.

Prerequisites

Before using Ansible on your Unraid system, certain prerequisites must be established to ensure that all Ansible modules function correctly. Each module may require specific Python packages installed via pip. However, since Unraid operates as an in-memory OS, any packages installed via the command line will be lost upon reboot. To address this limitation, you can modify the GO file to automatically install necessary packages during boot.

The GO file is executed during the startup process to initialize services on Unraid. By appending your installation steps to this file, you can customize the boot behavior of your host. Below is an example of a modified GO file that includes pip installation commands:

---
- name: "Create go file managed scripts directory"
  ansible.builtin.file:
    path: "/boot/config/managed-scripts"
    state: directory
    mode: "{{ item.mode | default('0777') }}"
    
- name: Template go managed files onto host
  template:
    src: "{{ item.src }}"
    dest: "{{ item.dest }}"
    mode: "{{ item.mode | default('0777') }}"
  with_items: "{{ go_file_managed_scripts }}"

- name: Add mappings to go file
  ansible.builtin.blockinfile:
    path: /boot/config/go
    block: |
      source {{ item.dest }}
    marker: "# {mark} ANSIBLE MANAGED BLOCK '{{ item.desc }}'"
  with_items: "{{ go_file_managed_scripts }}"

Once saved, this configuration ensures that your host is ready for Ansible access upon booting, whether you’re connecting from a local machine or a remote host. Use the following inventory configuration to connect to your Unraid host:

---
all:
  vars:
    ansible_python_interpreter: /usr/bin/python3
    ssh_common_args: "-o StrictHostKeyChecking=no"
  hosts:
    unraid:
      ansible_host: 192.168.0.10
      ansible_user: "root"
      ansible_ssh_pass: "root-pass"
      become: true

To verify access to your Unraid host, run this command with your configured host file:

$ ansible unraid -i /inventory.yml -m ping

Using Ansible on Unraid

Ansible provides robust modules for executing commands and managing tasks on target hosts. While Unraid features a user-friendly graphical interface and numerous built-in utilities, it remains a Linux-based OS that can be configured similarly to other systems.

With Ansible, you can automate various tasks including:

  • File manipulation (reading, writing)
  • User account management
  • Command execution (e.g., restarting services)
  • Software package management (installation, updates)
  • Configuration changes and security updates
  • Complex task automation (firewall setup, application deployment)

However, since Unraid is your NAS, exercise caution with certain operations and allow Unraid to handle critical functions. Always access cache storage under /mnt/user/cache and use /mnt/user/<mnt-name> for array writes to ensure proper functionality and prevent data loss during parity scans.

Managing Unraid Containers Using Ansible

Ansible allows you to define tasks for interacting with the Docker engine on your Unraid system efficiently:

---
- name: Start a webserver container
  community.docker.docker_container:
    name: whoami
    image: containous/whoami
    state: started
    recreate: true
    exposed_ports:
      - 8080:80

- name: Stop a webserver container
  community.docker.docker_container:
    name: whoami
    image: containous/whoami
    state: stopped
    recreate: true
    exposed_ports:
      - 8080:80

- name: Remove a webserver container
  community.docker.docker_container:
    name: whoami
    image: containous/whoami
    state: absent
    recreate: true
    exposed_ports:
      - 8080:80

These tasks utilize Ansible’s looping and templating capabilities, making container configurations adaptable based on environmental needs or provided settings. When deploying containers on Unraid, ensure you use the become flag for root-level command execution due to permission structures.

Using Docker-Compose Configuration Inline

In addition to managing individual containers, you can integrate Docker Compose with Ansible for dynamic configurations. This requires copying the Docker Compose file onto the remote host before execution:

- name: Inline docker-compose definition
  hosts: localhost
  gather_facts: false
  tasks:
  
  - name: Remove flask project
    community.docker.docker_compose:
      project_src: flask 
      state: absent

  - name: Start docker-compose project...
    community.docker.docker_compose:
      project_name: flask 
      definition:
        version: '2'
        services:
          db:
            image: postgres 
          web:
            build: "{{ playbook_dir }}/flask"
            command: "python manage.py runserver 0.0.0.0:8000"
            volumes:
              - "{{ playbook_dir }}/flask:/code"
            ports:
              - "8000:8000"
            depends_on:
              - db 
    register: output

  - name: Verify that the db and web services are running 
    ansible.builtin.assert:
      that:
        - "output.services.web.flask_web_1.state.running"
        - "output.services.db.flask_db_1.state.running"

Using Local Docker-Compose Configuration Files

Alternatively, you can template the Docker Compose file onto the remote host and execute it there:

- name: Run using a project directory 
  hosts: localhost 
  gather_facts: false 
  tasks:

  - name: Tear down existing services 
    community.docker.docker_compose:
      project_src: flask 
      state: absent 

  - name: Create and start services 
    community.docker.docker_compose:
      project_src: flask 
    register: output 

  - name: Show results 
    ansible.builtin.debug:
      var: output 

  - name: Run `docker-compose up` again 
    community.docker.docker_compose:
      project_src: flask 
      build: false 
    register: output 

  - name: Verify that web and db services are running 
    ansible.builtin.assert:
      that:
        - "output.services.web.flask_web_1.state.running"
        - "output.services.db.flask_db_1.state.running"

Note that any containers set to restart will maintain their state across reboots of both Unraid and Docker.

Monitoring Containers on Unraid

After deploying containers with Ansible using either method outlined above, you can monitor them through the UnRaid Docker GUI for a quick overview of running containers, access logs, and perform operations like start or stop. However, remember that any modifications made directly through the GUI will be overridden by your Ansible playbook during subsequent runs.

Third-party Options

Consider exploring third-party applications for enhanced monitoring capabilities of your containers. Tools like Portainer, Better Stack, and Middleware offer unique features that may benefit your container management strategy.

Wrapping Up

By now, you should have a clearer understanding of how to utilize Ansible for automating your Unraid systems effectively. With Ansible’s capabilities, you can create configuration files managed as code for consistent deployments across multiple machines. Explore further by checking out additional resources on installing apps via Ansible or leveraging Slackware packages to enhance your system’s functionality.