Cheatsheets

Ansible

Ansible

Comprehensive Ansible cheatsheet covering playbooks, inventories, roles, tasks, variables, handlers, ad-hoc commands, modules, and configuration options.

10 Categories36 Sections72 ExamplesPublished: 28 Feb, 2025Updated: 28 Feb, 2026

Ansible is a powerful automation tool for configuration management, application deployment, and orchestration. This cheatsheet provides quick reference guides for common Ansible tasks, modules, and best practices to help you get started and work efficiently with Ansible.

Getting Started

Installation & Setup

Install Ansible and configure basic settings

Install Ansible on Ubuntu

Code
Terminal window
sudo apt update
sudo apt install -y ansible

Install Ansible via pip

Code
Terminal window
pip install ansible
pip install ansible==2.10.7

Verify Ansible Installation

Displays installed Ansible version and configuration details

Code
Terminal window
ansible --version
Execution
Terminal window
ansible 2.10.7
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
executable location = /usr/bin/ansible
python version = 3.10.12
  • Version should be 2.9+
  • Python 3.6+ required

Ansible Configuration File

Main configuration file setup at ansible.cfg

Create Basic ansible.cfg

Code
[defaults]
inventory = ./hosts
remote_user = ubuntu
private_key_file = ~/.ssh/id_rsa
host_key_checking = False
gather_facts = True

Configure Parallel Execution

forks increases parallelism for faster execution

Code
[defaults]
forks = 10
timeout = 30
retries = 3
  • Default forks value is 5
  • Higher forks = more parallelism = higher memory usage

First Playbook Run

Execute your first Ansible playbook

Simple Hello World Playbook

Basic playbook that prints a debug message

Code
---
- name: Hello World
hosts: all
tasks:
- name: Print Hello
debug:
msg: "Hello from Ansible"
Execution
Terminal window
PLAY [Hello World] *****
TASK [Print Hello] *****
ok: [localhost] => {
"msg": "Hello from Ansible"
}
PLAY RECAP *****
localhost : ok=1 changed=0 failed=0
  • Playbooks must be valid YAML
  • [object Object]

Test Host Connectivity

Verify connectivity to managed hosts

Ping All Hosts

Tests connectivity to all hosts without running tasks

Code
Terminal window
ansible all -i hosts -m ping
Execution
Terminal window
webserver01 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
database01 | SUCCESS => {
"ping": "pong"
}
  • ping module requires Python on remote
  • SUCCESS indicates host is reachable

Inventory Management

Inventory Formats

Different ways to define managed hosts

INI Format Inventory

INI format with groups and variables

Code
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
[databases]
db1 ansible_host=192.168.1.20
db2 ansible_host=192.168.1.21
[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa
  • Groups defined with [groupname]
  • [all:vars] applies to all hosts

YAML Format Inventory

YAML format for complex hierarchies

Code
all:
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
web2:
ansible_host: 192.168.1.11
databases:
hosts:
db1:
ansible_host: 192.168.1.20
vars:
ansible_user: ubuntu
  • Better for nested groups
  • More readable for complex inventories

Host Patterns

Select specific hosts or groups

Target Specific Groups

webservers targets group, !web2 excludes host

Code
Terminal window
ansible webservers -i hosts -m ping
ansible databases -i hosts -m ping
ansible 'webservers:!web2' -i hosts -m ping
Execution
Terminal window
web1 | SUCCESS => {"ping": "pong"}
web2 | SUCCESS => {"ping": "pong"}
  • webservers selects entire group
  • negates selection

Wildcard and Range Patterns

Wildcards match patterns, [1:2] matches range

Code
Terminal window
ansible 'web*' -i hosts -m ping
ansible 'db[1:2]' -i hosts -m ping
ansible 'webservers[0]' -i hosts -m ping
  • * matches any characters
  • [1:2] includes indexes 1 and 2

Dynamic Inventory

Generate inventory from external sources

EC2 Dynamic Inventory

AWS EC2 plugin pulls instances from AWS

Code
plugin: aws_ec2
regions:
- us-east-1
filters:
tag:Environment: production
keyed_groups:
- key: placement.region_name
prefix: aws_region
  • Requires boto3 library
  • Cache results for performance

Custom Inventory Script

Custom script outputs JSON inventory format

Code
#!/bin/bash
cat << EOF
{
"webservers": {
"hosts": ["web1", "web2"]
}
}
EOF
  • Script must be executable
  • Output must be valid JSON

Host Variables & Priorities

Set variables at different levels

Host-Level Variables

Variables specific to individual hosts

Code
all:
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
http_port: 80
server_role: primary
  • Host variables override group variables
  • Highest precedence level

Group Variables File

Load variables from files by host/group name

Code
Terminal window
group_vars/webservers.yml
group_vars/webservers/main.yml
host_vars/web1.yml
  • group_vars/groupname.yml for entire group
  • host_vars/hostname.yml for single host

Ad-Hoc Commands

Basic Syntax

Run single tasks without playbook

Run Adhoc Command

Execute shell command on all hosts

Code
Terminal window
ansible all -i hosts -m shell -a "uptime"
Execution
Terminal window
web1 | CHANGED | rc=0 >>
10:45:23 up 5 days, 3:21, 2 users, load average: 0.05, 0.10, 0.08
web2 | CHANGED | rc=0 >>
10:45:24 up 3 days, 1:15, 1 user, load average: 0.02, 0.06, 0.05
  • -m specifies module
  • -a passes arguments

Use copy Module

Copy file from control node to remote

Code
Terminal window
ansible webservers -i hosts -m copy -a "src=/etc/hosts dest=/tmp/hosts"
Execution
Terminal window
web1 | CHANGED => {
"changed": true,
"checksum": "5d41402abc4b2a76b9719d911017c592",
"dest": "/tmp/hosts",
"gid": 0,
"mode": "0644",
"owner": "root",
"size": 158,
"src": "/tmp/ansible.87a8vz/hosts",
"state": "file"
}
  • src path on control node
  • dest path on remote

Common Ad-Hoc Modules

Frequently used modules for ad-hoc tasks

Package Management

Install nginx package

Code
Terminal window
ansible webservers -i hosts -m apt -a "name=nginx state=present"
Execution
Terminal window
web1 | CHANGED => {
"changed": true,
"stderr": "",
"stdout": "Reading package lists..."
}
  • apt for Debian/Ubuntu
  • yum for Red Hat/CentOS

Service Management

Start nginx service and enable on boot

Code
Terminal window
ansible webservers -i hosts -m service -a "name=nginx state=started enabled=yes"
Execution
Terminal window
web1 | CHANGED => {
"changed": true,
"enabled": true,
"name": "nginx",
"state": "started"
}
  • [object Object]
  • enabled=yes enables service on boot

File Permissions

Set file permissions and ownership

Code
Terminal window
ansible all -i hosts -m file -a "path=/tmp/test.txt mode=0644 owner=root"
Execution
Terminal window
web1 | CHANGED => {
"changed": true,
"mode": "0644",
"owner": "root",
"path": "/tmp/test.txt"
}
  • mode in octal format
  • owner and group can be specified

Parallelism & Forks

Control how many hosts execute simultaneously

Limit Concurrent Execution

Execute on 2 hosts at a time instead of default

Code
Terminal window
ansible all -i hosts -m ping -f 2
  • -f or --forks sets parallelism
  • Lower for safety, higher for speed

Serial Execution

Execute one at a time (serial)

Code
Terminal window
ansible all -i hosts -m shell -a "systemctl restart app" -f 1
  • Useful for rolling restarts
  • Prevents service downtime

Playbooks

Playbook Structure

Create and organize playbooks

Basic Playbook Structure

Complete playbook with all major sections

Code
---
- name: Deploy Web Application
hosts: webservers
become: yes
vars:
app_port: 8080
app_user: www-data
tasks:
- name: Install dependencies
apt:
name: python3-pip
state: present
- name: Start application
command: /opt/app/start.sh
  • --- indicates YAML document start
  • [object Object]
  • [object Object]

Multiple Plays in Single Playbook

Multiple plays execute sequentially

Code
---
- name: Configure Databases
hosts: databases
tasks:
- debug: msg="Setting up database"
- name: Configure Web Servers
hosts: webservers
tasks:
- debug: msg="Setting up web server"
  • Each play is independent
  • Plays execute in order

Playbook Execution

Run playbooks with various options

Run Basic Playbook

Execute playbook against specified inventory

Code
Terminal window
ansible-playbook playbook.yml -i hosts
Execution
Terminal window
PLAY [Deploy Web Application] *****
TASK [Install dependencies] *****
ok: [web1]
ok: [web2]
TASK [Start application] *****
changed: [web1]
changed: [web2]
PLAY RECAP *****
web1 : ok=2 changed=1 failed=0
web2 : ok=2 changed=1 failed=0
  • ok = task already in desired state
  • changed = task made changes

Check Mode (Dry Run)

Preview changes without executing

Code
Terminal window
ansible-playbook playbook.yml -i hosts --check
  • Shows what would change
  • Useful before production runs

Run with Extra Variables

Pass variables from command line

Code
Terminal window
ansible-playbook playbook.yml -i hosts -e "env=production app_version=2.0"
  • -e or --extra-vars for variables
  • Overrides defaults in playbook

Verbose Output

Show detailed execution information

Code
Terminal window
ansible-playbook playbook.yml -i hosts -vvv
  • -v = info, -vv = debug, -vvv = extra debug

Conditionals

Execute tasks based on conditions

Simple When Condition

Skip task if condition false

Code
- name: Install nginx if not exists
apt:
name: nginx
state: present
when: ansible_distribution == "Ubuntu"
Execution
Terminal window
TASK [Install nginx if not exists] *****
skipped: [web1] => (item=centos)
ok: [web2] => (item=ubuntu)
  • Conditions use when keyword
  • Access facts with ansible_variablename

Multiple Conditions

All conditions must be true (AND logic)

Code
- name: Restart service
service:
name: nginx
state: restarted
when:
- ansible_os_family == "Debian"
- ansible_distribution_version >= "20.04"
  • List format uses AND logic
  • [object Object]

Tasks & Handlers

Task Structure

Define and organize tasks

Basic Task Definition

Task with module, arguments, and tags

Code
- name: Install web server
apt:
name: nginx
state: present
become: yes
tags:
- webserver
- packages
  • [object Object]
  • [object Object]

Register Task Output

Save task output in variable for later use

Code
- name: Get service status
command: systemctl status nginx
register: nginx_status
changed_when: false
- name: Display status
debug:
msg: "{{ nginx_status.stdout }}"
  • register stores full result
  • Access with variable name

Handlers

Run tasks on change events

Basic Handler

Handler executes if task notifies it

Code
- name: Deploy application
hosts: webservers
tasks:
- name: Copy app config
copy:
src: app.conf
dest: /etc/app/app.conf
notify: restart app
handlers:
- name: restart app
service:
name: app
state: restarted
  • Handlers only run if task changed
  • Run at end of play by default

Multiple Handlers

Single task can notify multiple handlers

Code
tasks:
- name: Update config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify:
- reload nginx
- log config change
handlers:
- name: reload nginx
service:
name: nginx
state: reloaded
- name: log config change
command: logger "nginx config updated"
  • Handlers run in defined order
  • Duplicate handler calls only run once

Loops

Repeat tasks with different variables

Loop Over List

Item variable changes per loop iteration

Code
- name: Install multiple packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- php-fpm
- mysql-client
Execution
Terminal window
TASK [Install multiple packages] *****
ok: [web1] => (item=nginx)
ok: [web1] => (item=php-fpm)
ok: [web1] => (item=mysql-client)
  • [object Object]
  • item is implicit variable name

Loop Over Dict

Loop over dictionary structures

Code
- name: Create users
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
state: present
loop:
- { name: 'alice', uid: 1001 }
- { name: 'bob', uid: 1002 }
  • Access dict items with dot notation
  • Useful for complex configurations

Variables & Facts

Variable Definition

Define and use variables

Play-Level Variables

Define variables at play scope

Code
- name: My playbook
hosts: all
vars:
db_host: localhost
db_port: 3306
db_name: myapp
env: production
tasks:
- name: Connect to database
debug:
msg: "Connecting to {{ db_host }}:{{ db_port }}"
  • Accessible in all tasks
  • {{ }} for variable substitution

Task-Level Variables

Variables local to specific task

Code
- name: Configure service
service:
name: nginx
state: started
vars:
nginx_user: www-data
nginx_workers: 4
  • Only available in this task's context

Gathering Facts

Collect system information from hosts

Auto-Gather Facts

Facts gathered by default unless disabled

Code
- name: Display fact
hosts: all
tasks:
- name: Show OS
debug:
msg: "OS: {{ ansible_os_family }}, Memory: {{ ansible_memtotal_mb }}"
Execution
Terminal window
TASK [Show OS] *****
ok: [web1] => {
"msg": "OS: Debian, Memory: 4096"
}
  • ansible_* variables are facts
  • [object Object]

Custom Facts

Define custom facts in JSON files

Code
Terminal window
/etc/ansible/facts.d/custom.fact
{
"app_version": "2.1.0",
"deployment_date": "2026-01-15"
}
  • Store in /etc/ansible/facts.d/
  • Extension must be .fact

Variable Precedence

Order of variable resolution

Precedence Levels

Variables override based on source location

Code
# Lowest to highest precedence:
# 1. defaults/main.yml in role
# 2. group_vars/all
# 3. group_vars/groupname
# 4. host_vars/hostname
# 5. vars in playbook
# 6. vars_files
# 7. task vars
# 8. -e extra vars (highest)
  • Command line (-e) always wins
  • Host vars override group vars

Override with Extra Vars

Extra vars override all others

Code
Terminal window
ansible-playbook playbook.yml -e "db_host=newhost db_port=5432"
  • -e highest precedence
  • Useful for overriding defaults

Variable Templating

Template and manipulate variable values

Jinja2 Filters

Transform variables with filters

Code
- name: Use filters
debug:
msg: |
Uppercase: {{ service_name | upper }}
Lowercase: {{ domain | lower }}
Default: {{ undefined_var | default('localhost') }}
List join: {{ servers | join(',') }}
Execution
Terminal window
TASK [Use filters] *****
ok: [localhost] => {
"msg": "Uppercase: NGINX\nLowercase: example.com\nDefault: localhost\nList join: web1,web2,web3"
}
  • | for multiline strings
  • | filters modify variable values

Roles

Role Structure

Organize code with roles

Role Directory Structure

Standard role directory structure

Code
Terminal window
roles/webserver/
├── tasks/
└── main.yml
├── handlers/
└── main.yml
├── vars/
└── main.yml
├── defaults/
└── main.yml
├── files/
├── templates/
└── meta/
└── main.yml
  • tasks/ contains main task execution
  • defaults/ provides variable defaults

Role Definition

Define tasks in role

Code
roles/webserver/tasks/main.yml
---
- name: Install web server
apt:
name: nginx
state: present
become: yes
- name: Copy config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
  • Include handler definitions
  • Keep role single-purpose

Role Options & Includes

Include and configure roles

Include Role in Playbook

Include multiple roles

Code
- name: Deploy web application
hosts: webservers
roles:
- webserver
- php
- mysql
  • Roles execute in order
  • Most common role usage

Role with Variables

Pass variables to roles

Code
- name: Deploy web application
hosts: webservers
roles:
- role: webserver
vars:
nginx_port: 8080
nginx_workers: 8
- role: php
when: "'php' in group_names"
  • Vars override role defaults
  • when can conditionally include roles

Role Dependencies

Manage role dependencies

Declare Dependencies

Specify required roles

Code
roles/php/meta/main.yml
---
dependencies:
- role: webserver
- role: database
vars:
db_type: mysql
  • Dependencies execute first
  • Can pass variables to dependencies

Modules

System Modules

System administration modules

User Management

Create system user with specified properties

Code
- name: Create user
user:
name: appuser
uid: 2000
home: /opt/app
shell: /bin/bash
state: present
Execution
Terminal window
ok: [web1] => {
"changed": false,
"comment": "",
"home": "/opt/app",
"name": "appuser",
"shell": "/bin/bash",
"uid": 2000
}
  • [object Object]
  • uid specifies numeric user id

File Management

Create directory with permissions

Code
- name: Create directory
file:
path: /opt/app/data
state: directory
mode: '0755'
owner: appuser
recurse: yes
Execution
Terminal window
changed: [web1] => {
"changed": true,
"mode": "0755",
"owner": "appuser",
"path": "/opt/app/data",
"state": "directory"
}
  • [object Object]
  • [object Object]

Package Management

Install and manage packages

APT Package Manager

Install multiple packages with cache update

Code
- name: Install packages
apt:
name:
- curl
- wget
- git
state: present
update_cache: yes
  • update_cache updates apt database
  • [object Object]

PIP Package Manager

Install Python packages in virtualenv

Code
- name: Install Python packages
pip:
name:
- django==3.2
- requests>=2.25
virtualenv: /opt/app/venv
  • virtualenv creates/uses virtualenv
  • Version pinning with == or >=

Command Execution

Execute commands on remote hosts

Command Module (Preferred)

Execute command without shell processing

Code
- name: Get nginx version
command: nginx -v
register: nginx_version
changed_when: false
Execution
Terminal window
ok: [web1] => {
"cmd": ["nginx", "-v"],
"rc": 0,
"stderr": "nginx version: nginx/1.18.0",
"stdout": ""
}
  • No shell expansion (*, |, &)
  • More predictable

Shell Module

Execute with shell processing

Code
- name: Check if file exists
shell: |
if [ -f /opt/app/config.yml ]; then
echo "exists"
else
echo "missing"
fi
register: config_check
  • Supports pipes, redirects
  • Less secure, use with care

Web & Net Modules

HTTP and network operations

HTTP Request

Make HTTP request and validate response

Code
- name: Health check
uri:
url: http://localhost:8080/health
method: GET
status_code: 200
register: health_check
Execution
Terminal window
ok: [web1] => {
"changed": false,
"content": "{\"status\":\"ok\"}",
"status": 200
}
  • status_code validates response
  • body can parse JSON response

Advanced Features

Blocks & Error Handling

Group tasks and handle errors

Block with Error Handling

Block groups tasks with error handling

Code
- name: Database operations
block:
- name: Connect to database
debug: msg="Connecting"
- name: Run migrations
command: /opt/app/migrations.sh
rescue:
- name: Rollback on error
command: /opt/app/rollback.sh
always:
- name: Cleanup
file:
path: /tmp/app.lock
state: absent
  • [object Object]
  • [object Object]
  • [object Object]

Validate Task Results

Control when task is marked failed/changed

Code
- name: Run test
shell: npm test
failed_when: "npm_test.rc != 0"
changed_when: false
register: npm_test
  • failed_when override failure detection
  • changed_when override change detection

Asynchronous Execution

Run long-running tasks in background

Async with Polling

Run 1-hour task, check every 10 seconds

Code
- name: Long running task
shell: /opt/app/long-job.sh
async: 3600
poll: 10
register: long_job
Execution
Terminal window
ASYNC OK on web1 (job_id=123456789.12345)
  • [object Object]
  • [object Object]

Fire and Forget

Start task and don't wait for completion

Code
- name: Start background service
shell: /opt/app/service.sh
async: 0
poll: 0
  • async: 0 and poll: 0 for fire-and-forget

Advanced Variable Usage

Complex variable patterns

Access Host Variables

Access variables from other hosts

Code
- name: Reference another host
debug:
msg: "Database host: {{ hostvars['db1']['ansible_default_ipv4']['address'] }}"
  • hostvars dict contains all host info
  • Useful in multi-host plays

Nested Variable Access

Access deeply nested structures

Code
vars:
services:
web:
port: 8080
workers: 4
db:
port: 5432
replicas: 3
tasks:
- debug: msg="Web port {{ services.web.port }}"
  • Dot notation for nested access
  • Bracket notation also works

Plugins & Extensions

Extend Ansible functionality

Custom Filters

Apply custom filter plugins

Code
- name: Use custom filter
debug:
msg: "{{ 'hello' | custom_uppercase }}"
  • Place filters in filter_plugins/
  • Extend Jinja2 capabilities

Lookup Plugins

Use lookup plugins for dynamic data

Code
- name: Read file content
set_fact:
file_content: "{{ lookup('file', '/etc/config.yml') }}"
- name: Get environment variable
debug:
msg: "{{ lookup('env', 'HOME') }}"
  • file, env, pipe lookups common
  • Results available mid-playbook

Vault & Security

Vault Basics

Encrypt sensitive data

Create Vault File

Create encrypted YAML file

Code
Terminal window
ansible-vault create secrets.yml
# Enter password, then edit:
# db_password: "secret123"
# api_key: "abc123xyz"
Execution
Terminal window
New Vault password:
Confirm New Vault password:
  • Creates .yml with encrypted content
  • Prompts for password

View Vault Content

Decrypt and display vault file

Code
Terminal window
ansible-vault view secrets.yml
# Password prompt
  • Requires vault password
  • Doesn't save decrypted content

Edit Vault File

Edit encrypted vault file safely

Code
Terminal window
ansible-vault edit secrets.yml
  • Opens in editor
  • Re-encrypts on save

Using Vault in Playbooks

Reference vault variables in playbooks

Include Vault Variables

Load vault variables in playbook

Code
- name: Deploy app
hosts: webservers
vars_files:
- secrets.yml
tasks:
- name: Configure database
template:
src: db.conf.j2
dest: /etc/app/db.conf
vars:
db_password: "{{ vault_db_password }}"
  • [object Object]
  • Automatic decryption on run

Run Playbook with Vault

Run playbook requiring vault password

Code
Terminal window
ansible-playbook playbook.yml -i hosts --ask-vault-pass
Execution
Terminal window
Vault password:
PLAY [Deploy app] *****
TASK [Configure database] *****
ok: [web1]
  • --ask-vault-pass prompts for password
  • --vault-password-file for automation

Encrypt Individual Files

Encrypt specific files or variables

Encrypt Single Variable

Encrypt individual secret for playbook

Code
Terminal window
ansible-vault encrypt_string --ask-vault-pass 'mypassword'
Execution
Terminal window
Reading plaintext input from stdin.
New Vault password:
Confirm New Vault password:
!vault |
$ANSIBLE_VAULT;1.1;AES256
66386...
  • Generates encrypted string
  • Paste into playbook vars

Encrypt Specific Files

Encrypt individual variable files

Code
Terminal window
ansible-vault encrypt host_vars/prod.yml
  • Encrypts existing file in place

Vault Best Practices

Security practices with vault

Password File Not in Repo

Store vault password file locally, not in git

Code
Terminal window
.gitignore:
.vault_pass
Run playbook:
ansible-playbook playbook.yml --vault-password-file ~/.vault_pass
  • Keep .vault_pass outside repository
  • Use --vault-password-file for automation

Different Vaults for Environments

Separate encrypted files per environment

Code
Terminal window
group_vars/prod/secrets.yml (encrypted)
group_vars/dev/secrets.yml (encrypted)
group_vars/staging/secrets.yml (encrypted)
  • Different passwords per environment
  • Enhanced security isolation

You might also enjoy

Check out some of our other posts on similar topics

Chef

Chef is a powerful Infrastructure as Code platform for automating infrastructure configuration, deployment, and management. This cheatsheet provides comprehensive reference guides for Chef concepts, c

SSH

export const components = { h1: 'h1', h2: 'h2', h3: 'h3', h4: 'h4', h5: 'h5', h6: 'h6', p: 'p', a: 'a', ul: 'ul', ol: 'ol', li: 'li', code: 'code', pre: 'pre', strong: 'str

Docker Compose

Docker Compose is a tool for defining and running multi-container Docker applications. It uses a YAML file to configure application services, networks, and volumes. This cheatsheet provides a quick re

Dockerfile

This cheat sheet covers essential Dockerfile instructions, best practices, and common pitfalls for building efficient and secure Docker images. Whether you're new to Docker or looking to optimize your

PostgreSQL

Comprehensive PostgreSQL reference guide covering psql commands, database creation, tables, queries, functions, joins, transactions, indexes, and advanced SQL operations.

6 related posts