AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible for Database Automation: PostgreSQL, MySQL, MongoDB, and Redis

By Luca Berton · Published 2024-01-01 · Category: installation

Automate database deployment and management with Ansible. Install, configure, backup, replicate, and manage PostgreSQL, MySQL, MongoDB, and Redis at enterprise.

Introduction

Databases are the backbone of every application, and managing them at scale requires automation. Ansible provides modules for installing, configuring, backing up, and managing the most popular database engines — without needing agents on database servers. From initial deployment through replication setup to automated backups, Ansible handles the full database lifecycle.

See also: Automating PostgreSQL Configuration with Ansible Setting Maximum Connections

PostgreSQL

Install and Configure

---
- name: Deploy PostgreSQL
  hosts: postgresql_servers
  become: true
  vars:
    pg_version: 16
    pg_data_dir: /var/lib/postgresql/{{ pg_version }}/main
    pg_max_connections: 200
    pg_shared_buffers: 4GB
    pg_work_mem: 64MB
  tasks:
    - name: Install PostgreSQL
      ansible.builtin.apt:
        name:
          - "postgresql-{{ pg_version }}"
          - "postgresql-client-{{ pg_version }}"
          - python3-psycopg2
        state: present

    - name: Configure postgresql.conf
      ansible.builtin.lineinfile:
        path: "/etc/postgresql/{{ pg_version }}/main/postgresql.conf"
        regexp: "^#?{{ item.key }}"
        line: "{{ item.key }} = {{ item.value }}"
      loop:
        - { key: listen_addresses, value: "'*'" }
        - { key: max_connections, value: "{{ pg_max_connections }}" }
        - { key: shared_buffers, value: "{{ pg_shared_buffers }}" }
        - { key: work_mem, value: "{{ pg_work_mem }}" }
        - { key: effective_cache_size, value: "12GB" }
        - { key: maintenance_work_mem, value: "512MB" }
        - { key: wal_level, value: "replica" }
        - { key: max_wal_senders, value: "5" }
        - { key: archive_mode, value: "on" }
        - { key: log_statement, value: "'ddl'" }
      notify: restart postgresql

    - name: Configure pg_hba.conf
      community.postgresql.postgresql_pg_hba:
        dest: "/etc/postgresql/{{ pg_version }}/main/pg_hba.conf"
        contype: host
        databases: "{{ item.db }}"
        users: "{{ item.user }}"
        source: "{{ item.source }}"
        method: scram-sha-256
      loop:
        - { db: all, user: all, source: "10.0.0.0/8" }
        - { db: replication, user: replicator, source: "10.0.0.0/8" }
      notify: reload postgresql

  handlers:
    - name: restart postgresql
      ansible.builtin.systemd:
        name: postgresql
        state: restarted
    - name: reload postgresql
      ansible.builtin.systemd:
        name: postgresql
        state: reloaded

Create Databases and Users

    - name: Create application database
      community.postgresql.postgresql_db:
        name: "{{ item }}"
        encoding: UTF-8
        lc_collate: en_US.UTF-8
        lc_ctype: en_US.UTF-8
      become_user: postgres
      loop:
        - myapp_production
        - myapp_staging

    - name: Create application user
      community.postgresql.postgresql_user:
        name: app_user
        password: "{{ vault_db_password }}"
        role_attr_flags: NOSUPERUSER,NOCREATEDB
      become_user: postgres
      no_log: true

    - name: Grant privileges
      community.postgresql.postgresql_privs:
        database: myapp_production
        roles: app_user
        type: database
        privs: CONNECT
      become_user: postgres

    - name: Grant schema privileges
      community.postgresql.postgresql_privs:
        database: myapp_production
        roles: app_user
        type: schema
        objs: public
        privs: ALL
      become_user: postgres

PostgreSQL Replication

- name: Configure PostgreSQL streaming replication
  hosts: pg_replica
  become: true
  tasks:
    - name: Stop PostgreSQL
      ansible.builtin.systemd:
        name: postgresql
        state: stopped

    - name: Clear data directory
      ansible.builtin.file:
        path: "{{ pg_data_dir }}"
        state: absent

    - name: Base backup from primary
      ansible.builtin.command: >
        pg_basebackup -h {{ pg_primary_host }}
        -D {{ pg_data_dir }}
        -U replicator -Fp -Xs -P -R
      become_user: postgres

    - name: Start PostgreSQL (replica mode)
      ansible.builtin.systemd:
        name: postgresql
        state: started

MySQL

Install and Secure

- name: Deploy MySQL
  hosts: mysql_servers
  become: true
  tasks:
    - name: Install MySQL
      ansible.builtin.apt:
        name:
          - mysql-server
          - python3-pymysql
        state: present

    - name: Start MySQL
      ansible.builtin.systemd:
        name: mysql
        state: started
        enabled: true

    - name: Set root password
      community.mysql.mysql_user:
        name: root
        password: "{{ vault_mysql_root_pass }}"
        host: localhost
        login_unix_socket: /var/run/mysqld/mysqld.sock
      no_log: true

    - name: Remove anonymous users
      community.mysql.mysql_user:
        name: ''
        host_all: true
        state: absent
        login_user: root
        login_password: "{{ vault_mysql_root_pass }}"

    - name: Remove test database
      community.mysql.mysql_db:
        name: test
        state: absent
        login_user: root
        login_password: "{{ vault_mysql_root_pass }}"

    - name: Create application database
      community.mysql.mysql_db:
        name: myapp
        encoding: utf8mb4
        collation: utf8mb4_unicode_ci
        login_user: root
        login_password: "{{ vault_mysql_root_pass }}"

    - name: Create application user
      community.mysql.mysql_user:
        name: app_user
        password: "{{ vault_mysql_app_pass }}"
        host: "10.0.%"
        priv: "myapp.*:ALL"
        login_user: root
        login_password: "{{ vault_mysql_root_pass }}"
      no_log: true

    - name: Configure MySQL
      ansible.builtin.template:
        src: my.cnf.j2
        dest: /etc/mysql/mysql.conf.d/custom.cnf
      notify: restart mysql

See also: Ansible for PostgreSQL and MySQL: Database Automation Complete Guide

MongoDB

Deploy Replica Set

- name: Deploy MongoDB Replica Set
  hosts: mongodb_servers
  become: true
  vars:
    mongo_version: "7.0"
    mongo_replset: "rs0"
  tasks:
    - name: Install MongoDB
      ansible.builtin.apt:
        name: mongodb-org
        state: present

    - name: Configure MongoDB
      ansible.builtin.template:
        src: mongod.conf.j2
        dest: /etc/mongod.conf
      notify: restart mongod

    - name: Start MongoDB
      ansible.builtin.systemd:
        name: mongod
        state: started
        enabled: true

- name: Initialize replica set
  hosts: mongodb_servers[0]
  tasks:
    - name: Initialize replica set
      community.mongodb.mongodb_replicaset:
        login_host: localhost
        login_port: 27017
        replica_set: "{{ mongo_replset }}"
        members:
          - host: "{{ groups['mongodb_servers'][0] }}:27017"
            priority: 2
          - host: "{{ groups['mongodb_servers'][1] }}:27017"
            priority: 1
          - host: "{{ groups['mongodb_servers'][2] }}:27017"
            priority: 1

    - name: Create admin user
      community.mongodb.mongodb_user:
        database: admin
        name: admin
        password: "{{ vault_mongo_admin_pass }}"
        roles:
          - { db: admin, role: userAdminAnyDatabase }
          - { db: admin, role: clusterAdmin }
        replica_set: "{{ mongo_replset }}"
      no_log: true

Redis

Deploy with Sentinel HA

- name: Deploy Redis
  hosts: redis_servers
  become: true
  tasks:
    - name: Install Redis
      ansible.builtin.apt:
        name: redis-server
        state: present

    - name: Configure Redis
      ansible.builtin.template:
        src: redis.conf.j2
        dest: /etc/redis/redis.conf
      notify: restart redis

    - name: Start Redis
      ansible.builtin.systemd:
        name: redis-server
        state: started
        enabled: true

- name: Configure Redis Sentinel
  hosts: redis_sentinel
  become: true
  tasks:
    - name: Configure Sentinel
      ansible.builtin.template:
        src: sentinel.conf.j2
        dest: /etc/redis/sentinel.conf
      notify: restart sentinel
# templates/redis.conf.j2
bind {{ ansible_default_ipv4.address }} 127.0.0.1
port 6379
requirepass {{ vault_redis_password }}
maxmemory {{ redis_maxmemory | default('2gb') }}
maxmemory-policy allkeys-lru

{% if redis_role == 'replica' %}
replicaof {{ redis_primary_host }} 6379
masterauth {{ vault_redis_password }}
{% endif %}

# Security
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command DEBUG ""

See also: Automate PostgreSQL Backups with Ansible Playbook

Automated Backups

- name: Database backup schedule
  hosts: database_servers
  become: true
  tasks:
    - name: PostgreSQL backup
      ansible.builtin.cron:
        name: "PostgreSQL daily backup"
        hour: "2"
        minute: "0"
        job: >
          pg_dump -Fc myapp_production >
          /backup/pg-myapp-$(date +\%Y\%m\%d).dump &&
          find /backup -name 'pg-myapp-*.dump' -mtime +30 -delete
        user: postgres
      when: "'postgresql' in group_names"

    - name: MySQL backup
      ansible.builtin.cron:
        name: "MySQL daily backup"
        hour: "2"
        minute: "0"
        job: >
          mysqldump --all-databases --single-transaction
          -u backup -p'{{ vault_mysql_backup_pass }}' |
          gzip > /backup/mysql-all-$(date +\%Y\%m\%d).sql.gz &&
          find /backup -name 'mysql-all-*.sql.gz' -mtime +30 -delete
      when: "'mysql' in group_names"

    - name: MongoDB backup
      ansible.builtin.cron:
        name: "MongoDB daily backup"
        hour: "2"
        minute: "0"
        job: >
          mongodump --out /backup/mongo-$(date +\%Y\%m\%d)
          -u backup -p '{{ vault_mongo_backup_pass }}' --authenticationDatabase admin &&
          find /backup -maxdepth 1 -name 'mongo-*' -type d -mtime +30 -exec rm -rf {} +
      when: "'mongodb' in group_names"

Required Collections

ansible-galaxy collection install community.postgresql
ansible-galaxy collection install community.mysql
ansible-galaxy collection install community.mongodb
ansible-galaxy collection install community.general

pip install psycopg2-binary pymysql pymongo redis

Best Practices

  1. Always use no_log: true for password-related tasks
  2. Replication before backups — High availability first, then backup the replica
  3. Test restores — A backup you can't restore is worthless
  4. Parameterize tuning — Use Ansible variables for memory/connection settings per environment
  5. Encrypt connections — TLS/SSL for all database connections
  6. Separate backup user — Dedicated read-only user for backups
  7. Monitor replication lag — Alert when replicas fall behind
  8. Use connection pooling — Deploy PgBouncer/ProxySQL alongside databases

FAQ

Can Ansible handle database migrations?

Ansible can run migration commands (alembic upgrade head, flask db upgrade, rails db:migrate), but dedicated migration tools provide better rollback and versioning.

How to handle large databases?

Use pg_basebackup for PostgreSQL, xtrabackup for MySQL — both support online, non-blocking backups that Ansible can orchestrate.

Should databases be in containers?

For production: bare metal or VMs are generally preferred for performance and data persistence. Containers work well for dev/test environments.

Conclusion

Ansible provides comprehensive database automation — from initial deployment through replication setup, security hardening, and automated backups. By managing PostgreSQL, MySQL, MongoDB, and Redis through playbooks, you ensure consistent, reproducible database infrastructure across all environments.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home