Creating a Self-Hosted Environment with Docker and Ansible

As a software developer, having a reliable and efficient development environment is crucial to ensure a smooth and seamless workflow. However, relying on cloud-based solutions can be costly and may not be as flexible as having a self-hosted setup. That's why I decided to create my own self-hosted development environment using Docker and Ansible, and it has been a game-changer for me.

Choosing the Right Hardware and Operating System

The first step in establishing a self-hosted environment is selecting the appropriate hardware and operating system. I chose the Dell OptiPlex 3090 MFF Mini PC with an Intel® CoreTM i7-10700T, 32GB DDR4 RAM, 1TB HDD, and 512GB SSD. I installed Ubuntu 22.04 as the server operating system because I previously used Ubuntu as a server operating system, and these server resources are sufficient for my needs.

Moving the Development Environment to the Server

My initial plan was to move my development environment to the server, but I soon realized that I was only utilizing a small fraction of the server's resources for that purpose. It felt like a waste to let such a capable machine sit idle most of the time, so I decided to explore other ways to leverage it. That's when I thought of hosting some helpful services for my home network. The first service that I decided to set up was Jellyfin, which serves as a media library. By doing so, I not only made the most of the server's resources, but I also added a lot of other helpful services to my daily life, such as Jellyfin for media library, Transmission for downloading torrents, a file browser for easy access to files, and automated backups for my MacBook.

Using Ansible and Docker for Delivery and Reproducibility

Ansible is a powerful tool for automating server configuration and management, while Docker provides a lightweight and efficient way to run applications in containers. I used Ansible to set up the server and Docker to run the applications.

My Final Setup

After some tweaking and fine-tuning, my self-hosted development environment now consists of the following components:

Jellyfin

  1. Jellyfin – is a great alternative to Plex since it allows me to construct a home media library that is accessible from any device in my home network.

Jellyfin

Ansible playbook
- hosts: all
  become: yes
  tasks:
    - name: Deploy Jellyfin
      community.docker.docker_container:
        name: jellyfin
        image: jellyfin/jellyfin:latest
        ports:
          - 8096:8096
        volumes:
          - /srv/jellyfin/config:/config
          - /srv/jellyfin/cache:/cache
          - /mnt/sda1/media:/media
          - /dev/shm:/transcode
        devices:
          - /dev/dri:/dev/dri
        restart_policy: always

Transmission

  1. Transmission is a web-based version of the Transmission BitTorrent client, via which I may download torrents.

transmission

Ansible playbook
- hosts: all
  become: yes
  tasks:
	- name: Deploy Transmission
	  community.docker.docker_container:
		name: transmission
		image: lscr.io/linuxserver/transmission:latest
		ports:
		- 9091:9091
		- 51413:51413
		- 51413:51413/udp
		volumes:
		- /srv/transmission/config:/config
		- /mnt/sda1/downloads:/downloads
		- /mnt/sda1/downloads:/watch

Filebrowser

  1. Filebrowser – a web file browser that lets you manage files on the media server and upload photos to the media library.

file browser

Ansible playbook
- hosts: all
  become: yes
  tasks:
    - name: Deploy Filebrowser
      community.docker.docker_container:
        name: filebrowser
        image: filebrowser/filebrowser
        ports:
          - 9092:80
        volumes:
          - /mnt/sda1/:/srv
          - /srv/fb/filebrowser.db:/database.db
        user: "0:0"
        restart_policy: always

Portainer

  1. Portainer – is a web-based graphical user interface (GUI) that simplifies the management of Docker environments. With Portainer, users can easily deploy, manage, and monitor Docker containers, images, networks, and volumes from a single dashboard.

Ansible playbook
- hosts: all
  become: yes
  tasks:
    - name: Deploy Portainer
      community.docker.docker_container:
        name: portainer
        image: portainer/portainer-ce
        ports:
          - "9000:9000"
          - "8000:8000"
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
          - portainer_data:/data
        restart_policy: always

Nginx Proxy Manager

  1. Nginx Proxy Manager is a fantastic tool for streamlining the process of managing subdomains for various services. When paired with a domain acquired from a registrar such as Cloudflare, the process becomes even more seamless. Traffic to the domain is forwarded to the server by adding an A record for '@' to the server's IP address. Nginx Proxy Manager is then used to generate subdomains for each service that are easily accessible and manageable. Another useful feature of Nginx Proxy Manager is the ability to produce Let's Encrypt SSL certificates for each subdomain, allowing for secure and encrypted access to each service. Overall, this application has greatly simplified my workflow by allowing me to manage and access all of my services from a single domain.

nginx proxy manager

Ansible playbook
- hosts: all
  become: yes
  tasks:
    - name: Deploy nginx proxy manager
      community.docker.docker_container:
        name: nginx-proxy-manager
        image: 'jc21/nginx-proxy-manager:latest'
        ports:
          - '80:80'
          - '81:81'
          - '443:443'
        volumes:
          - ./data:/data
          - ./letsencrypt:/etc/letsencrypt
        restart_policy: always

Watchtower

  1. Watchtower – with watchtower, you can simply submit a new image to Docker Hub or your own image registry to update the running version of your containerized software. Simply put, it upgrades running containers to the most recent versions on a regular basis.
Ansible playbook
- hosts: all
  become: yes
  tasks:
    - name: Update docker containers
      community.docker.docker_container:
        name: watchtower
        image: containrrr/watchtower
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
        env:
          WATCHTOWER_POLL_INTERVAL=86400
        restart_policy: always

Homepage

  1. Homepage – A modern (fully static, fast), secure (fully proxied), highly customizable application dashboard with integrations for more than 25 services and translations for over 15 languages.

Homepage

Ansible playbook
- hosts: all
  become: yes
  tasks:
    - name: Dashboard
      community.docker.docker_container:
        name: dashboard
        image: ghcr.io/benphelps/homepage:latest
        network_mode: host
        ports:
          - 3000:3000
        volumes:
          - /srv/dashboard/config:/app/config
          - /mnt/sda1:/mnt/sda1
          - /mnt/nvme0n1p3:/mnt/nvme0n1p3
        restart_policy: always

Dash

  1. Dash – is a modern server dashboard, running on the latest tech, designed with glassmorphism in mind. It is intended to be used for smaller VPS and private servers.

dash

Ansible playbook
- hosts: all
  become: yes
  tasks:
    - name: Dashbot
      community.docker.docker_container:
        name: dashdot
        image: mauricenino/dashdot
        ports:
          - 3001:3001
        volumes:
          - /:/mnt/host:ro
        env:
          DASHDOT_CUSTOM_HOST=psi
          DASHDOT_SHOW_HOST=true
        restart_policy: always

Time machine

  1. Time Machine is the built-in backup feature of your Mac, to automatically back up your personal data, including apps, music, photos, email, and documents. Having a backup allows you to restore your Mac from a Time Machine backup if you ever delete your files or can't access them.

Here is my /etc/samba/smb.conf samba configuration

[global]
workgroup = myhostname
min protocol = SMB2

security = user
passdb backend = tdbsam
map to guest = Bad User

spotlight = yes
vfs objects = acl_xattr catia fruit streams_xattr
fruit:aapl = yes
fruit:time machine = yes

[timemachine]
comment = Time Machine
path = /mnt/nvme0n1p3
valid users = timemachine
browsable = yes
writable = yes
read only = no
create mask = 0644
directory mask = 0755

Conclusion

Using Docker and Ansible to create a self-hosted environment has been a game changer for me. It has enabled me to fully utilize the capabilities of my server, saving me money and giving me better flexibility and control. I definitely recommend giving it a try if you're a software developer or simply looking for a superb home server setup with media library and backup server. It's easier than ever to manage and monitor your containers and services using tools like Portainer and Nginx Proxy Manager.

Discuss...