Docker Compose Guide: Multi-Container Application Management
Define and manage multi-container applications with Docker Compose: service dependencies, environment variables, health checks, scaling, and production deployment. Comprehensive guide.
Ansible installation, inventory file, playbook writing, role structure, variables, encrypted vault, ad-hoc commands, and server configuration automation. Step-by-step guide.
Ansible is an open-source tool that lets you manage server configuration, application deployment, and task automation as code. Thanks to its agentless architecture, it only connects to target servers via SSH.
Key features of Ansible:
Ansible connects from the control node to target servers over SSH. WinRM is used for Windows servers.
# Add PPA repository
sudo apt update
sudo apt install software-properties-common -y
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install ansible -y
# Check version
ansible --version
# Add EPEL repository
sudo dnf install epel-release -y
sudo dnf install ansible -y
# Alternative: install via pip
pip3 install ansible
# Create Python virtual environment
python3 -m venv ansible-env
source ansible-env/bin/activate
# Install Ansible and additional tools
pip install ansible ansible-lint
# Check version
ansible --version
Inventory defines the servers Ansible will manage:
# /etc/ansible/hosts or custom inventory file
# Single server
web1.example.com
# Group definition
[webservers]
web1.example.com
web2.example.com ansible_port=2222
192.168.1.10 ansible_user=ubuntu
[databases]
db1.example.com
db2.example.com
# Group variables
[webservers:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3
# Parent group
[production:children]
webservers
databases
# inventory.yml
all:
children:
webservers:
hosts:
web1.example.com:
ansible_port: 22
web2.example.com:
ansible_port: 2222
vars:
ansible_user: deploy
http_port: 80
databases:
hosts:
db1.example.com:
db2.example.com:
vars:
ansible_user: dbadmin
db_port: 5432
Run commands without writing a playbook for quick tasks:
# Ping test
ansible all -m ping
ansible webservers -m ping -i inventory.yml
# Run command
ansible all -m command -a "uptime"
ansible webservers -m shell -a "df -h"
# Copy file
ansible webservers -m copy -a "src=/local/file dest=/remote/path"
# Install package
ansible webservers -m apt -a "name=nginx state=present" --become
# Manage service
ansible webservers -m service -a "name=nginx state=started enabled=yes" --become
# Create/delete file
ansible all -m file -a "path=/tmp/test state=touch"
ansible all -m file -a "path=/tmp/test state=absent"
# Gather facts
ansible web1.example.com -m setup
ansible all -m setup -a "filter=ansible_os_family"
# site.yml
---
- name: Configure web servers
hosts: webservers
become: yes # use sudo
vars:
http_port: 80
max_clients: 200
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Copy Nginx configuration
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: Restart Nginx
- name: Start Nginx service
service:
name: nginx
state: started
enabled: yes
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
# Conditional tasks
- name: Install package on Ubuntu
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- curl
- git
when: ansible_os_family == "Debian"
- name: Install package on RHEL
dnf:
name: "{{ item }}"
state: present
loop:
- nginx
- curl
- git
when: ansible_os_family == "RedHat"
# Create users with loop
- name: Create users
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop:
- { name: 'alice', groups: 'sudo' }
- { name: 'bob', groups: 'www-data' }
# Register and condition
- name: Check Nginx version
command: nginx -v
register: nginx_version
ignore_errors: yes
- name: Install Nginx if not installed
apt:
name: nginx
state: present
when: nginx_version.rc != 0
# group_vars/webservers.yml
http_port: 80
https_port: 443
app_user: www-data
app_dir: /var/www/html
# host_vars/web1.example.com.yml
ansible_host: 192.168.1.10
server_name: web1.example.com
# Variable usage in playbook
- name: Create application directory
file:
path: "{{ app_dir }}"
state: directory
owner: "{{ app_user }}"
mode: '0755'
# Using facts
- name: Show system information
debug:
msg: |
OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
CPU: {{ ansible_processor_vcpus }} cores
RAM: {{ ansible_memtotal_mb }} MB
IP: {{ ansible_default_ipv4.address }}
To encrypt sensitive data:
# Create encrypted file
ansible-vault create secrets.yml
# Encrypt existing file
ansible-vault encrypt vars/passwords.yml
# Edit encrypted file
ansible-vault edit secrets.yml
# View encrypted file
ansible-vault view secrets.yml
# Remove encryption
ansible-vault decrypt vars/passwords.yml
# Change password
ansible-vault rekey secrets.yml
# Run playbook with vault
ansible-playbook site.yml --ask-vault-pass
ansible-playbook site.yml --vault-password-file ~/.vault_pass
# secrets.yml (encrypted content)
db_password: "secret_database_password"
api_key: "secret_api_key"
ssl_cert_key: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
# Create role
ansible-galaxy init roles/nginx
# Role directory structure
roles/nginx/
├── defaults/
│ └── main.yml # Default variables
├── files/
│ └── nginx.conf # Static files
├── handlers/
│ └── main.yml # Handlers
├── meta/
│ └── main.yml # Role metadata
├── tasks/
│ └── main.yml # Main tasks
├── templates/
│ └── vhost.conf.j2 # Jinja2 templates
└── vars/
└── main.yml # Variables
# roles/nginx/tasks/main.yml
---
- name: Install Nginx
package:
name: nginx
state: present
- name: Configure Nginx
template:
src: vhost.conf.j2
dest: /etc/nginx/sites-available/{{ site_name }}
notify: Reload Nginx
- name: Enable site
file:
src: /etc/nginx/sites-available/{{ site_name }}
dest: /etc/nginx/sites-enabled/{{ site_name }}
state: link
notify: Reload Nginx
---
- name: Nginx web server installation
hosts: webservers
become: yes
vars:
domain: example.com
web_root: /var/www/{{ domain }}
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Create web directory
file:
path: "{{ web_root }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Create sample index.html
copy:
content: "<h1>Hello from {{ inventory_hostname }}</h1>"
dest: "{{ web_root }}/index.html"
owner: www-data
- name: Configure virtual host
template:
src: vhost.conf.j2
dest: /etc/nginx/sites-available/{{ domain }}
notify: Reload Nginx
- name: Enable virtual host
file:
src: /etc/nginx/sites-available/{{ domain }}
dest: /etc/nginx/sites-enabled/{{ domain }}
state: link
notify: Reload Nginx
- name: Disable default site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Reload Nginx
- name: Allow Nginx through UFW
ufw:
rule: allow
name: 'Nginx Full'
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloaded
---
- name: User management
hosts: all
become: yes
vars_files:
- vars/users.yml
tasks:
- name: Create sudo group
group:
name: sudo
state: present
- name: Create users
user:
name: "{{ item.username }}"
comment: "{{ item.full_name }}"
groups: "{{ item.groups | join(',') }}"
shell: /bin/bash
create_home: yes
state: present
loop: "{{ users }}"
- name: Add SSH public key
authorized_key:
user: "{{ item.username }}"
key: "{{ item.ssh_key }}"
state: present
loop: "{{ users }}"
when: item.ssh_key is defined
- name: Grant sudo access
copy:
content: "{{ item.username }} ALL=(ALL) NOPASSWD:ALL"
dest: /etc/sudoers.d/{{ item.username }}
mode: '0440'
loop: "{{ users }}"
when: item.sudo | default(false)
# Basic run
ansible-playbook site.yml
# With specific inventory
ansible-playbook -i inventory.yml site.yml
# Specific host or group
ansible-playbook site.yml --limit webservers
ansible-playbook site.yml --limit web1.example.com
# Dry-run (check without making changes)
ansible-playbook site.yml --check
# Verbose mode
ansible-playbook site.yml -v
ansible-playbook site.yml -vvv
# Run specific tags
ansible-playbook site.yml --tags "nginx,ssl"
ansible-playbook site.yml --skip-tags "debug"
# Extra variables
ansible-playbook site.yml -e "env=production version=1.5"
Ansible is one of the most accessible ways to manage server automation as code. With its agentless architecture, large module library, and YAML-based structure, you can easily manage both small and large-scale infrastructure. Keep sensitive data secure with Vault while creating reusable configurations with roles.
Ansible works agentlessly (only SSH required), while Puppet and Chef require agent installation on target servers. Ansible is YAML-based and easier to learn. Puppet and Chef offer more powerful state management but are more complex to set up and learn. Ansible is generally preferred for small to medium-scale infrastructure.
Idempotency means running the same operation multiple times always produces the same result. Ansible modules are designed to be idempotent: if nginx is already installed it won't reinstall it, if a file is already correct it won't change it. This allows you to safely re-run playbooks.
If the vault password is forgotten, encrypted files cannot be recovered. This is why it's critical to store the vault password in a secure password manager (1Password, Bitwarden, etc.). In production environments, it's recommended to write the vault password to a file (--vault-password-file) and store it securely.
Ansible Galaxy is the official platform where community-created roles and collections are shared (galaxy.ansible.com). You can download ready-made roles with commands like 'ansible-galaxy install geerlingguy.nginx'. This lets you use tested roles instead of writing from scratch.
Increase the 'forks' value in ansible.cfg (default 5, 20-50 recommended). Reduce SSH connection count with 'pipelining = True'. Enable independent hosts to run in parallel with 'strategy: free'. Control batch size with 'serial' in large inventories.
Yes, Ansible can manage Windows servers via WinRM (Windows Remote Management) protocol. 'ansible_connection: winrm' and 'ansible_winrm_transport: ntlm' settings are required. Windows modules start with the 'win_' prefix: win_package, win_service, win_file, etc.
Define and manage multi-container applications with Docker Compose: service dependencies, environment variables, health checks, scaling, and production deployment. Comprehensive guide.
Build CI/CD pipelines with GitHub Actions: Docker build and push, SSH server deployment, test automation, secrets management, and workflow optimization.
Caddy web server installation, Caddyfile configuration, automatic Let's Encrypt SSL, reverse proxy, static site hosting, PHP-FPM integration, and performance settings.