Introduction

The ansible.builtin.git module clones and updates Git repositories on remote hosts. Combined with SSH key authentication, it's the standard way to deploy application code, configuration repos, and infrastructure-as-code across server fleets.

For HTTPS checkout, see Checkout git repository via HTTPS.

Module Parameters

ParameterTypeRequiredDescription
repostringYesRepository URL (SSH or HTTPS)
destpathYesDestination directory on remote
versionstringNoBranch, tag, or commit SHA (default: HEAD)
key_filepathNoPath to SSH private key on remote host
accept_hostkeyboolNoAuto-accept unknown SSH host keys
updateboolNoPull new revisions if repo exists (default: true)
forceboolNoDiscard local changes before updating
depthintNoShallow clone depth (saves bandwidth)
recursiveboolNoInitialize submodules (default: true)
single_branchboolNoClone only the specified branch
cloneboolNoIf false, only update existing repo
bareboolNoCreate a bare repository

Basic Clone via SSH

---
- name: Deploy application code
  hosts: app_servers
  become: false
  vars:
    repo: "git@github.com:myorg/myapp.git"
    dest: "/home/deploy/myapp"
  tasks:
    - name: Ensure git is installed
      ansible.builtin.yum:
        name: git
        state: present
      become: true

    - name: Clone repository
      ansible.builtin.git:
        repo: "{{ repo }}"
        dest: "{{ dest }}"
        key_file: "~/.ssh/id_ed25519"
        accept_hostkey: true

SSH Key Setup

Prerequisites

The SSH private key must exist on the remote host (not the controller). The corresponding public key must be added to your Git server.

Deploy SSH Key with Ansible

- name: Deploy SSH key for Git access
  ansible.builtin.copy:
    content: "{{ vault_git_deploy_key }}"
    dest: /home/deploy/.ssh/id_ed25519
    owner: deploy
    group: deploy
    mode: '0600'
  no_log: true

- name: Add GitHub to known_hosts
  ansible.builtin.known_hosts:
    name: github.com
    key: "{{ lookup('pipe', 'ssh-keyscan github.com 2>/dev/null') }}"
    path: /home/deploy/.ssh/known_hosts
    state: present

Use Agent Forwarding (Alternative)

Instead of deploying keys to every host, forward your local SSH agent:

# ansible.cfg
[ssh_connection]
ssh_args = -o ForwardAgent=yes
- name: Clone using forwarded agent
  ansible.builtin.git:
    repo: "git@github.com:myorg/myapp.git"
    dest: /opt/myapp
    # No key_file needed — uses forwarded agent

Practical Patterns

Deploy Specific Branch or Tag

- name: Deploy release v2.1.0
  ansible.builtin.git:
    repo: "git@github.com:myorg/myapp.git"
    dest: /opt/myapp
    version: "v2.1.0"
    key_file: ~/.ssh/deploy_key

Deploy Specific Commit

- name: Pin to exact commit
  ansible.builtin.git:
    repo: "git@github.com:myorg/myapp.git"
    dest: /opt/myapp
    version: "a1b2c3d4e5f6"
    key_file: ~/.ssh/deploy_key

Shallow Clone (Save Bandwidth)

- name: Shallow clone (latest only)
  ansible.builtin.git:
    repo: "git@github.com:myorg/myapp.git"
    dest: /opt/myapp
    depth: 1
    single_branch: true
    version: main
    key_file: ~/.ssh/deploy_key

Force Update (Discard Local Changes)

- name: Force clean checkout
  ansible.builtin.git:
    repo: "git@github.com:myorg/myapp.git"
    dest: /opt/myapp
    force: true
    key_file: ~/.ssh/deploy_key

Clone with Submodules

- name: Clone with submodules
  ansible.builtin.git:
    repo: "git@github.com:myorg/myapp.git"
    dest: /opt/myapp
    recursive: true
    key_file: ~/.ssh/deploy_key

Complete Deployment Playbook

---
- name: Deploy application from Git
  hosts: app_servers
  become: true
  vars:
    app_repo: "git@github.com:myorg/myapp.git"
    app_version: "main"
    app_dir: "/opt/myapp"
    app_user: "myapp"

  tasks:
    - name: Ensure dependencies installed
      ansible.builtin.yum:
        name: [git, python3, python3-pip]
        state: present

    - name: Create app user
      ansible.builtin.user:
        name: "{{ app_user }}"
        system: true
        home: "{{ app_dir }}"
        shell: /bin/bash

    - name: Deploy SSH key
      ansible.builtin.copy:
        content: "{{ vault_deploy_key }}"
        dest: "/home/{{ app_user }}/.ssh/id_ed25519"
        owner: "{{ app_user }}"
        mode: '0600'
      no_log: true

    - name: Clone application
      ansible.builtin.git:
        repo: "{{ app_repo }}"
        dest: "{{ app_dir }}"
        version: "{{ app_version }}"
        key_file: "/home/{{ app_user }}/.ssh/id_ed25519"
        accept_hostkey: true
        force: true
      become_user: "{{ app_user }}"
      register: git_result
      notify: restart app

    - name: Install Python dependencies
      ansible.builtin.pip:
        requirements: "{{ app_dir }}/requirements.txt"
        virtualenv: "{{ app_dir }}/venv"
      become_user: "{{ app_user }}"
      when: git_result.changed

    - name: Show deployed version
      ansible.builtin.debug:
        msg: "Deployed commit: {{ git_result.after }}"

  handlers:
    - name: restart app
      ansible.builtin.systemd:
        name: myapp
        state: restarted

Return Values

ValueDescription
afterCommit SHA after the operation
beforeCommit SHA before the operation
remote_url_changedWhether the remote URL changed
warningsList of warnings during operation

Common Errors

"Permission denied (publickey)"

fatal: Could not read from remote repository.
Please make sure you have the correct access rights.

Fix: Verify key_file path exists on the remote host, has 0600 permissions, and the public key is added to the Git server.

"Host key verification failed"

Fix: Add accept_hostkey: true or pre-populate known_hosts.

"Destination directory not empty"

Fix: Use force: true to overwrite, or ensure dest is either empty or a valid git repo.

Conclusion

Use ansible.builtin.git with key_file for SSH clones, depth: 1 for faster deploys, version for pinning branches/tags/commits, and force: true when local changes should be discarded. Deploy SSH keys with copy + no_log: true, or use agent forwarding. The after return value gives you the deployed commit SHA for tracking.