⚠️ CRITICAL: Version Policy Change
H2O no longer uses version tagging since January 2020. The master branch is considered stable. Each commit is treated as a release.
⚠️ CRITICAL: Debian Package Removal (May 2025)
H2O has been removed from Debian testing/unstable due to lack of a maintainer. The Debian Bookworm package has 5 unpatched CVEs.
This playbook builds H2O from source for secure, up-to-date deployments.
- name: Install H2O from source
hosts: h2o
become: true
vars:
h2o_src_dir: "/usr/local/src/h2o"
h2o_install_dir: "/usr/local"
h2o_config_dir: "/etc/h2o"
h2o_data_dir: "/var/lib/h2o"
h2o_log_dir: "/var/log/h2o"
h2o_user: "h2o"
h2o_group: "h2o"
tasks:
- name: Warn about version policy
ansible.builtin.debug:
msg: |
⚠️ H2O VERSION POLICY
- No version tagging since January 2020
- Master branch is considered stable
- Each commit is treated as a release
- Building from master branch
- name: Install build dependencies (Debian/Ubuntu)
ansible.builtin.apt:
name:
- build-essential
- cmake
- pkg-config
- libssl-dev
- zlib1g-dev
- git
state: present
update_cache: true
when: ansible_os_family == "Debian"
- name: Install build dependencies (RHEL family)
ansible.builtin.dnf:
name:
- gcc
- make
- cmake
- pkg-config
- openssl-devel
- zlib-devel
- git
state: present
when: ansible_os_family == "RedHat"
- name: Create h2o user
ansible.builtin.user:
name: "{{ h2o_user }}"
group: "{{ h2o_group }}"
system: true
shell: /usr/sbin/nologin
create_home: false
- name: Clone H2O source repository
ansible.builtin.git:
repo: "https://github.com/h2o/h2o.git"
dest: "{{ h2o_src_dir }}"
version: "master"
recursive: true
register: git_result
- name: Create build directory
ansible.builtin.file:
path: "{{ h2o_src_dir }}/build"
state: directory
mode: "0755"
- name: Configure H2O build with CMake
ansible.builtin.command:
cmd: "cmake -DCMAKE_INSTALL_PREFIX={{ h2o_install_dir }} .."
chdir: "{{ h2o_src_dir }}/build"
register: cmake_result
changed_when: "'Configuring done' in cmake_result.stdout"
- name: Build H2O
ansible.builtin.command:
cmd: "make -j{{ ansible_processor_vcpus | default(2) }}"
chdir: "{{ h2o_src_dir }}/build"
register: make_result
changed_when: make_result.rc == 0
- name: Install H2O
ansible.builtin.command:
cmd: "make install"
chdir: "{{ h2o_src_dir }}/build"
become: true
register: install_result
changed_when: install_result.rc == 0
- name: Create configuration directory
ansible.builtin.file:
path: "{{ h2o_config_dir }}"
state: directory
mode: "0755"
owner: root
group: root
- name: Create data directory
ansible.builtin.file:
path: "{{ h2o_data_dir }}"
state: directory
mode: "0755"
owner: "{{ h2o_user }}"
group: "{{ h2o_group }}"
- name: Create log directory
ansible.builtin.file:
path: "{{ h2o_log_dir }}"
state: directory
mode: "0755"
owner: "{{ h2o_user }}"
group: adm
- name: Deploy systemd service file
ansible.builtin.copy:
dest: "/etc/systemd/system/h2o.service"
mode: "0644"
content: |
[Unit]
Description=H2O - HTTP/1, HTTP/2, HTTP/3 Server
After=network.target
[Service]
Type=simple
User={{ h2o_user }}
Group={{ h2o_group }}
ExecStart={{ h2o_install_dir }}/bin/h2o -c {{ h2o_config_dir }}/h2o.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
notify: reload_systemd
- name: Deploy minimal H2O configuration
ansible.builtin.copy:
dest: "{{ h2o_config_dir }}/h2o.conf"
mode: "0644"
content: |
listen:
port: 80
hosts:
"default":
paths:
"/":
file.dir: /var/www/html
file.index: [index.html]
- name: Create default web root
ansible.builtin.file:
path: /var/www/html
state: directory
mode: "0755"
owner: "{{ h2o_user }}"
group: "{{ h2o_group }}"
- name: Create default index.html
ansible.builtin.copy:
dest: /var/www/html/index.html
mode: "0644"
content: |
<!DOCTYPE html>
<html>
<head><title>H2O Server</title></head>
<body>
<h1>H2O is running</h1>
<p>HTTP/1, HTTP/2, HTTP/3 (QUIC) support enabled</p>
</body>
</html>
- name: Enable and start H2O service
ansible.builtin.systemd:
name: h2o
state: started
enabled: true
daemon_reload: true
- name: Verify H2O installation
ansible.builtin.command: "{{ h2o_install_dir }}/bin/h2o --version"
register: h2o_version
changed_when: false
- name: Display H2O version
ansible.builtin.debug:
var: h2o_version.stdout
- name: Show installation summary
ansible.builtin.debug:
msg: |
✅ H2O Installation Complete
Version: {{ h2o_version.stdout }}
Config: {{ h2o_config_dir }}/h2o.conf
Web Root: /var/www/html
Service: Active and enabled
⚠️ Note: H2O uses master branch (no version tagging since 2020)
handlers:
- name: reload_systemd
ansible.builtin.systemd:
daemon_reload: true
⚠️ Warning: Only use this for RHEL 9 with OKey repository or isolated test environments. Debian packages have unpatched CVEs.
- name: Install H2O from package (legacy)
hosts: h2o
become: true
vars:
h2o_config_dir: /etc/h2o
tasks:
- name: Warn about package issues
ansible.builtin.debug:
msg: |
⚠️ PACKAGE WARNING
- Debian: Package removed May 2025, unpatched CVEs
- RHEL: Only available from OKey repository
- Consider building from source instead
- name: Install H2O package (Debian - legacy)
ansible.builtin.apt:
name: h2o
state: present
update_cache: true
when: ansible_os_family == "Debian"
failed_when: false
- name: Install H2O package (RHEL - OKey repo)
ansible.builtin.dnf:
name: h2o
state: present
when: ansible_os_family == "RedHat"
failed_when: false
- name: Enable and start service
ansible.builtin.systemd:
name: h2o
state: started
enabled: true
For containerized deployments:
- name: Deploy H2O via Docker
hosts: h2o
become: true
vars:
h2o_compose_dir: "/opt/h2o"
tasks:
- name: Install Docker dependencies
ansible.builtin.package:
name:
- docker-ce
- docker-compose-plugin
state: present
- name: Create H2O compose directory
ansible.builtin.file:
path: "{{ h2o_compose_dir }}"
state: directory
mode: "0755"
- name: Deploy Docker Compose file
ansible.builtin.copy:
dest: "{{ h2o_compose_dir }}/docker-compose.yml"
mode: "0644"
content: |
services:
h2o:
image: micrograils/h2o:latest
ports:
- "80:80"
- "443:443"
- "8443:8443/udp" # HTTP/3 QUIC
volumes:
- ./config:/etc/h2o
- ./www:/var/www
restart: unless-stopped
- name: Start H2O container
ansible.builtin.command: "docker compose up -d"
args:
chdir: "{{ h2o_compose_dir }}"
register: compose_result
changed_when: "'Started' in compose_result.stdout or 'created' in compose_result.stdout"
| Distribution | Status | Notes |
|---|---|---|
| Debian 12 (Bookworm) | ⚠️ Available (unpatched CVEs) | 5 known vulnerabilities |
| Debian 13 (Trixie) | ❌ Removed (May 2025) | No maintainer |
| Ubuntu 22.04/24.04 | ⚠️ May be outdated | Check PPA status |
| RHEL 9 | ✅ OKey repository | Version 2.2.6 |
| Fedora | ❌ Not in EPEL | Build from source |
/etc/h2o/h2o.confh2o -t -c /etc/h2o/h2o.confsudo systemctl reload h2ocurl -I http://localhostSee H2O Configuration for configuration details.
⚠️ Security Warning: Debian packages have unpatched vulnerabilities.
See h2o Security and H2O Hardening for security guidance.
Beyond this playbook, we offer:
Contact our automation team: office@linux-server-admin.com