diff --git a/ansible/README.md b/ansible/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..eb7c656fb9957f17ba112c3b15c41fe9b7380e70
--- /dev/null
+++ b/ansible/README.md
@@ -0,0 +1,38 @@
+# Automated deployment
+
+Ansible playbook for automated deployment on the online cluster
+
+## Setup Ansible
+
+```bash
+$ git clone https://github.com/ansible/ansible.git
+$ cd ansible
+$ git checkout v2.8.1
+```
+
+## Activate the environment
+
+```bash
+$ source ansible/hacking/env-setup
+```
+
+Alternatively you can use the provided environment on `exflgateway`
+
+```bash
+$ source /opt/karabo/ansible/hacking/env-setup
+```
+
+## Python dependencies
+
+Ensure `yaml` and `jinja2` packages are installed in your Python environment. If not:
+
+```bash
+pip install pyYaml jinja2
+```
+
+## Install pycalibration
+
+```bash
+./install.yml GROUP_NAME
+```
+
diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..aaf6908b306a5957f35abc4eb59b3cb91e4b2e7e
--- /dev/null
+++ b/ansible/ansible.cfg
@@ -0,0 +1,8 @@
+[defaults]
+forks = 30
+inventory = ./hosts
+stdout_callback = debug
+
+[ssh_connection]
+pipelining = True
+ssh_args = -o ControlMaster=auto -o ControlPersist=1200
\ No newline at end of file
diff --git a/ansible/group_vars/HED b/ansible/group_vars/HED
new file mode 100644
index 0000000000000000000000000000000000000000..ad8d8aaf2498940349b61d2025011f1bef18e054
--- /dev/null
+++ b/ansible/group_vars/HED
@@ -0,0 +1,5 @@
+---
+ansible_user: "xcal"
+install_dir: "/scratch/xcal"
+pycal_dir: "{{ install_dir }}/pycalibration"
+pycal_version: "3.14.3"
diff --git a/ansible/hosts b/ansible/hosts
new file mode 100644
index 0000000000000000000000000000000000000000..dc4410f1182af3cd5758f49dc0384fcb41b3c716
--- /dev/null
+++ b/ansible/hosts
@@ -0,0 +1,3 @@
+[HED]
+sa2-onc-0[1:6]
+sa2-onc-hed
\ No newline at end of file
diff --git a/ansible/install.yml b/ansible/install.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0048d799a4ba09793e272e5f62f6cf27aa08c314
--- /dev/null
+++ b/ansible/install.yml
@@ -0,0 +1,71 @@
+#!play
+
+- hosts: all
+
+  tasks:
+  - name: Find all backup directory
+    find:
+      paths: "{{ install_dir }}"
+      patterns: "^pycalibration_backup_\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$"
+      file_type: directory
+      use_regex: True
+    register: backup_dirs
+
+  - name: Display matching directories
+    debug:
+      msg: "Matching directories: {{ backup_dirs.files | map(attribute='path') | list }}"
+
+  - name: Sort backup directories
+    set_fact:
+      sorted_backup_dirs: "{{ backup_dirs.files | sort(attribute='mtime', reverse=true) | list }}"
+
+  - name: Delete all but the most recent backup
+    file:
+      path: "{{ item.path }}"
+      state: absent
+    loop: "{{ sorted_backup_dirs[1:] }}"
+    when: sorted_backup_dirs | length > 1
+    register: deleted_dirs
+
+  - name: Information about backup retention
+    debug:
+      msg: |
+        Kept directory: {{ sorted_backup_dirs[0].path if sorted_backup_dirs else 'None' }}
+        Deleted directories ({{ deleted_dirs.results | length }}):
+        {% for dir in deleted_dirs.results %}
+        - {{ dir.path }}
+        {% endfor %}
+
+  - name: Get current date
+    command: date +%Y-%m-%dT%H:%M:%S
+    register: current_date
+    changed_when: false
+
+  - name: Set backup directory name
+    set_fact:
+      backup_dir_name: "pycalibration_backup_{{ current_date.stdout }}"
+
+  - name: Create backup directory
+    file:
+      state: directory
+      path: "{{ install_dir }}/{{ backup_dir_name }}"
+      mode: 0755
+    register: backup_dir
+
+  - name: Check for previous installation
+    stat:
+      path: "{{ pycal_dir }}"
+    register: pycal_dir_stat
+
+  - name: Backup previous installation
+    command: "mv {{ pycal_dir }} {{ backup_dir.path }}"
+    when: pycal_dir_stat.stat.exists
+
+  - name: Clone pycalibration
+    shell: git clone ssh://git@git.xfel.eu:10022/calibration/pycalibration.git -b {{ pycal_version }} {{ pycal_dir }}
+
+  - name: create venv
+    shell: /gpfs/exfel/sw/calsoft/.pyenv/versions/3.8.18/bin/python3 -m venv --prompt pycal-{{ pycal_version }} {{ pycal_dir }}/.venv
+
+  - name: Install Pycalibration
+    shell: all_proxy="http://exflproxy01.desy.de:3128" {{ pycal_dir }}/.venv/bin/pip install -e {{ pycal_dir }}
diff --git a/ansible/play b/ansible/play
new file mode 100644
index 0000000000000000000000000000000000000000..f1dfe48c472e5bc33248d4bde72390cff41f03d4
--- /dev/null
+++ b/ansible/play
@@ -0,0 +1,10 @@
+#!/bin/bash
+if [[ $# -lt 2 ]]
+then
+ echo "USAGE: $1 GROUP_NAME"
+ exit 1
+fi
+PLAYBOOK="$1"
+shift
+exec ansible-playbook "$PLAYBOOK" -l "$@" 
+