← Back to h2o/h2o

How to Deploy & Use h2o/h2o

# H2O HTTP Server Deployment & Usage Guide

## 1. Prerequisites

### System Requirements
- **OS**: Linux (kernel 3.10+), macOS (10.14+), or FreeBSD 10+
- **Architecture**: x86_64, ARM64, or ARMv7
- **Memory**: 512MB minimum (2GB+ recommended for production with HTTP/3)

### Build Dependencies
- **C Compiler**: GCC 4.9+ or Clang 3.4+
- **CMake**: 3.0 or higher
- **SSL Library** (one of):
 - OpenSSL 1.0.2+ (1.1.1+ recommended for TLS 1.3)
 - LibreSSL 2.4+
 - BoringSSL (for QUIC/HTTP3 support)
- **zlib**: 1.2.3+ (for compression)
- **libuv**: 1.0+ (optional, for event loop integration)
- **pkg-config**: For dependency detection

### Optional Dependencies
- **Ruby**: 2.0+ (required for mruby scripting support)
- **Perl**: 5.10+ (required for running test suite)
- **libcap**: For binding privileged ports without root (Linux)

### Install Dependencies (Platform-Specific)

**Ubuntu/Debian:**
```bash
sudo apt-get update
sudo apt-get install build-essential cmake libssl-dev zlib1g-dev libuv1-dev pkg-config ruby perl

macOS:

xcode-select --install
brew install cmake openssl zlib libuv pkg-config ruby

CentOS/RHEL/Fedora:

sudo yum groupinstall "Development Tools"
sudo yum install cmake openssl-devel zlib-devel libuv-devel pkgconfig ruby perl

2. Installation

Clone Repository

git clone --depth 1 https://github.com/h2o/h2o.git
cd h2o

Configure Build

Create build directory and run CMake:

mkdir -p build
cd build

Standard Build (with OpenSSL):

cmake ..

Build with LibreSSL (bundled, recommended for HTTP/3):

cmake -DWITH_BUNDLED_SSL=on ..

Build with mruby support:

cmake -DWITH_MRUBY=on ..

Production Optimized Build:

cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local ..

Compile and Install

make -j$(nproc)
sudo make install

Verify Installation

h2o --version

3. Configuration

H2O uses YAML configuration files. Create a configuration file at /etc/h2o/h2o.conf or ~/.h2o.conf.

Basic Configuration Example

Create /etc/h2o/h2o.conf:

# Listen on HTTP and HTTPS
listen:
  port: 80
listen:
  port: 443
  ssl:
    key-file: /etc/ssl/private/example.com.key
    certificate-file: /etc/ssl/certs/example.com.crt
    minimum-version: TLSv1.2

# HTTP/3 (QUIC) - requires OpenSSL 3.0+ or BoringSSL
listen:
  port: 443
  type: quic
  ssl:
    key-file: /etc/ssl/private/example.com.key
    certificate-file: /etc/ssl/certs/example.com.crt

# Virtual Host Configuration
hosts:
  "example.com:80":
    paths:
      /:
        file.dir: /var/www/html
        file.send-compressed: on
    
  "example.com:443":
    paths:
      /:
        file.dir: /var/www/html
        file.send-compressed: on
        file.mime.addtypes:
          "application/javascript; charset=utf-8": .js

# Logging
access-log: /var/log/h2o/access.log
error-log: /var/log/h2o/error.log
pid-file: /var/run/h2o.pid

# Performance Tuning
http1-upgrade-to-http2: on
http2-reprioritize-blocking-assets: on   # HTTP/2 push optimization
file.io_uring: on                        # Linux io_uring support (kernel 5.6+)

Environment Variables

  • H2O_ROOT: Installation prefix (default: /usr/local)
  • H2O_CONF: Path to configuration file (default: /etc/h2o/h2o.conf)
  • SSL_CERT_FILE / SSL_CERT_DIR: SSL certificate lookup paths

SSL Certificate Setup

For HTTP/3 (QUIC), certificates must include the TLS extension for QUIC:

# Generate with proper key usage for QUIC
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 \
  -extensions v3_req -subj "/CN=example.com"

4. Build & Run

Development Mode

Build with debugging symbols and test suite:

cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_MRUBY=on ..
make -j$(nproc)
make test

Running Locally

Start with custom configuration:

# Foreground mode (for testing)
sudo h2o -c /path/to/h2o.conf -m daemon

# Debug mode (verbose logging, foreground)
sudo h2o -c /path/to/h2o.conf --debug

Using as a Library

Link against libh2o in your C project:

#include <h2o.h>

// Initialize event loop
h2o_context_t ctx;
h2o_config_init(&conf);
h2o_context_init(&ctx, loop, &conf);

Compile with:

gcc -o myapp myapp.c `pkg-config --cflags --libs libh2o`

5. Deployment

Systemd Service Setup

Create /etc/systemd/system/h2o.service:

[Unit]
Description=H2O HTTP Server
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/h2o -c /etc/h2o/h2o.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5

# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/h2o /var/run

# Allow binding to privileged ports without root
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable h2o
sudo systemctl start h2o

Docker Deployment

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y build-essential cmake libssl-dev zlib1g-dev git
RUN git clone --depth 1 https://github.com/h2o/h2o.git /tmp/h2o && \
    cd /tmp/h2o && mkdir build && cd build && \
    cmake -DCMAKE_BUILD_TYPE=Release -DWITH_BUNDLED_SSL=on .. && \
    make -j$(nproc) && make install
COPY h2o.conf /etc/h2o/h2o.conf
EXPOSE 80 443/udp 443/tcp
CMD ["h2o", "-c", "/etc/h2o/h2o.conf"]

Run with:

docker build -t h2o-server .
docker run -p 80:80 -p 443:443/tcp -p 443:443/udp h2o-server

HTTP/3 (QUIC) Production Setup

Ensure firewall allows UDP on port 443:

# iptables
sudo iptables -A INPUT -p udp --dport 443 -j ACCEPT

# nftables
sudo nft add rule inet filter input udp dport 443 accept

Reverse Proxy Configuration

H2O as reverse proxy to backend application:

hosts:
  "api.example.com":
    paths:
      /:
        proxy.reverse.url: http://127.0.0.1:8080/
        proxy.websocket: on
        proxy.preserve-x-forwarded-proto: on
        proxy.timeout.io: 30000ms

6. Troubleshooting

Build Issues

CMake cannot find OpenSSL:

# Specify OpenSSL path explicitly
cmake -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl ..

Missing libuv (optional): If libuv is not found, H2O will use its internal event loop. To force libuv:

cmake -DWITH_LIBUV=on ..

Runtime Issues

Permission Denied on Ports < 1024: H2O needs CAP_NET_BIND_SERVICE capability or run as root (not recommended for production):

sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/h2o

HTTP/3 Connection Failures:

  • Verify OpenSSL 3.0+ or BoringSSL is used (check h2o --version)
  • Ensure UDP port 443 is open in firewall (not just TCP)
  • Check SSL certificate includes proper ALPN for h3

High Memory Usage:

  • Disable HTTP/2 push if not needed: http2-push: off
  • Reduce http2-max-concurrent-requests-per-connection (default: 100)
  • Enable file.send-compressed: on to reduce memory pressure

Configuration Syntax Errors: Test configuration before reloading:

sudo h2o -c /etc/h2o/h2o.conf -t

Log Rotation: H2O supports USR1 signal for log reopening:

logrotate -f /etc/logrotate.d/h2o  # Sends USR1 to h2o

Security Hardening

  • Run with user www-data: user: www-data in config
  • Enable strict transport security:
    header.add: "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload"
    
  • Limit request body size: limit-request-body: 102400000 (100MB)

Performance Tuning

For high-throughput deployments:

# /etc/h2o/h2o.conf
max-connections: 10240
http2-max-concurrent-requests-per-connection: 200
file.io_uring: on          # Linux 5.6+ only
file.send-compressed: gzip
gzip: on