# ArkHost Hetzner VPS Module

WHMCS server module for Hetzner Cloud VPS management.

## Features

**Core**

- VPS lifecycle: create, suspend, unsuspend, terminate
- Server control: start, stop, restart, shutdown
- Automatic provisioning on payment

**Advanced**

- Cloud-init support for automated server configuration
- Floating IP management with reverse DNS
- Backup creation and restoration
- Firewall rule management
- Performance graphs (CPU, network, disk I/O)
- Rescue mode with temporary passwords
- ISO mounting
- OS reinstallation

**Interface**

- Bootstrap 4 responsive design
- Multiple language translations included
- Real-time status updates
- VNC console access

## Requirements

- WHMCS 8.9+
- PHP 7.4+
- Hetzner Cloud API token
- cURL extension
- HTTPS connection

## Installation

1. Upload files to `/path/to/whmcs/modules/servers/ArkHostHetznerVPS/`
2. Upload `hooks.php` to `/path/to/whmcs/includes/hooks/` (for enhanced UI)
3. Create server and add it to a server group:
    - Setup → Products/Services → Servers → Create New Group
    - Name: `Hetzner Cloud`
    - Type: `ArkHostHetznerVPS`
    - Hostname: `localhost`
    - Username: Hetzner Project ID (optional)
    - Password: Hetzner Cloud API Token
4. Create products:
    - Setup → Products/Services → Products/Services → Create New Product
    - Type: `VPS/Dedicated Server`
    - Module: `ArkHostHetznerVPS`
    - Server Group: Select Hetzner Cloud

## Configuration

**Module Settings**

- Server Type: `cx23`, `cx33`, `cx43`, `cx53` (Cost-Optimized), `cpx11`, `cpx21`, `cax11`, `ccx13`, etc.
- Operating System: `ubuntu-22.04`, `debian-11`, `rocky-9`, etc.
- Location: `fsn1`, `nbg1`, `hel1`, `ash`, `hil`, `sin`
- Backups: On/Off
- Create Floating IP: On/Off
- Cloud-Init YAML: Optional custom cloud-init configuration

**Custom Field (Required)**

- Field Name: `ArkHostHetznerVPS|VPS ID`
- Type: Text Box
- Admin Only: Yes

For ModulesGarden migration, keep existing `serverID|Server ID` field.

## Configurable Options

Create in Setup → Products/Services → Configurable Options:

**1\. Floating IP Add-on**

- Option Name: `Floating IP`
- Option Type: `Yes/No`
- Options:
    - `No|None`
    - `Yes|1 Floating IP`

**2\. Location Selection**

- Option Name: `Location`
- Option Type: `Dropdown`
- Options:
    - `fsn1|Falkenstein, Germany`
    - `nbg1|Nuremberg, Germany`
    - `hel1|Helsinki, Finland`
    - `ash|Ashburn, USA`
    - `hil|Hillsboro, USA`
    - `sin|Singapore, Singapore`

Existing configurable options named `datacenter` remain supported. Legacy values such as `fsn1-dc14` are automatically converted to their corresponding location (`fsn1`) when provisioning.

**3\. Server Type**

- Option Name: `planid`
- Option Type: `Dropdown`
- Options:
- `cx23|CX23 - 2 vCPU, 4 GB RAM, 40 GB Disk, shared (Cost-Optimized)`
- `cx33|CX33 - 4 vCPU, 8 GB RAM, 80 GB Disk, shared (Cost-Optimized)`
- `cx43|CX43 - 8 vCPU, 16 GB RAM, 160 GB Disk, shared (Cost-Optimized)`
- `cx53|CX53 - 16 vCPU, 32 GB RAM, 320 GB Disk, shared (Cost-Optimized)`
- `cpx11|CPX 11 - 2 vCPU, 2 GB RAM, 40 GB Disk, shared`
- `cpx21|CPX 21 - 3 vCPU, 4 GB RAM, 80 GB Disk, shared`
- `cpx31|CPX 31 - 4 vCPU, 8 GB RAM, 160 GB Disk, shared`
- `cpx41|CPX 41 - 8 vCPU, 16 GB RAM, 240 GB Disk, shared`
- `cpx51|CPX 51 - 16 vCPU, 32 GB RAM, 360 GB Disk, shared`
- `cax11|CAX11 - 2 vCPU, 4 GB RAM, 40 GB Disk, shared`
- `cax21|CAX21 - 4 vCPU, 8 GB RAM, 80 GB Disk, shared`
- `cax31|CAX31 - 8 vCPU, 16 GB RAM, 160 GB Disk, shared`
- `cax41|CAX41 - 16 vCPU, 32 GB RAM, 320 GB Disk, shared`
- `ccx13|CCX13 Dedicated CPU - 2 vCPU, 8 GB RAM, 80 GB Disk, dedicated`
- `ccx23|CCX23 Dedicated CPU - 4 vCPU, 16 GB RAM, 160 GB Disk, dedicated`
- `ccx33|CCX33 Dedicated CPU - 8 vCPU, 32 GB RAM, 240 GB Disk, dedicated`
- `ccx43|CCX43 Dedicated CPU - 16 vCPU, 64 GB RAM, 360 GB Disk, dedicated`
- `ccx53|CCX53 Dedicated CPU - 32 vCPU, 128 GB RAM, 600 GB Disk, dedicated`
- `ccx63|CCX63 Dedicated CPU - 48 vCPU, 192 GB RAM, 960 GB Disk, dedicated`
- `cx22|CX22 - 2 vCPU, 4 GB RAM, 40 GB Disk, shared`
- `cx32|CX32 - 4 vCPU, 8 GB RAM, 80 GB Disk, shared`
- `cx42|CX42 - 8 vCPU, 16 GB RAM, 160 GB Disk, shared`
- `cx52|CX52 - 16 vCPU, 32 GB RAM, 320 GB Disk, shared`


**4\. Operating System**

- Option Name: `osid`
- Option Type: `Dropdown`
- Options:
- `debian-11|Debian 11`
- `debian-12|Debian 12`
- `debian-13|Debian 13`
- `ubuntu-22.04|Ubuntu 22.04`
- `ubuntu-24.04|Ubuntu 24.04`
- `rocky-8|Rocky Linux 8`
- `rocky-9|Rocky Linux 9`
- `rocky-10|Rocky Linux 10`
- `alma-8|AlmaLinux 8`
- `alma-9|AlmaLinux 9`
- `alma-10|AlmaLinux 10`
- `centos-stream-9|CentOS Stream 9`
- `centos-stream-10|CentOS Stream 10`
- `opensuse-15|openSUSE 15`
- `fedora-41|Fedora 41`
- `fedora-42|Fedora 42`
- `docker-ce|Docker CE`
- `lamp|LAMP Stack`
- `wordpress|WordPress`
- `nextcloud|Nextcloud`
- `gitlab|GitLab`
- `jitsi|Jitsi`
- `wireguard|WireGuard`
- `prometheus-grafana|Prometheus + Grafana`
- `owncast|Owncast`
- `photoprism|PhotoPrism`
- `rustdesk|RustDesk`


  

## Client Interface

**Overview**: Server status, information, control buttons **Graphs**: CPU, network, disk I/O monitoring **Backups**: Create, restore, delete backups **Settings**: Hostname, ISO, password reset, reinstall, firewall, rescue mode, floating IP

## SSH Key Management

**Why no SSH key feature?**

Hetzner's API stores SSH keys at the project level, not per-server. In a multi-tenant WHMCS environment, this creates security risks:

- Potential key exposure across customers
- No proper isolation between services
- Complex workarounds that compromise security

**Current recommended approach:**

1. Use password authentication for initial access
2. Add your SSH key manually: `ssh-copy-id root@your-server-ip`
3. Secure your server:

```bash
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_configsystemctl restart sshd
```

This ensures proper key isolation and security for each customer.

**Future development:**

We're exploring secure implementations using cloud-init or per-server user data to enable SSH key management while maintaining proper isolation.

## Cloud-Init Support

Cloud-init allows automatic server configuration during first boot. Unlike project-level SSH keys, cloud-init user_data is per-server, ensuring proper isolation in multi-tenant environments.

**Configuration:**

1. Navigate to Setup → Products/Services → Products/Services
2. Edit your product → Module Settings tab
3. Find "Cloud-Init YAML (Optional)" textarea
4. Enter your cloud-init configuration in YAML format
5. Leave empty to skip cloud-init

**Common Use Cases:**

**1. Change APT Mirrors** (original use case - avoid Hetzner mirrors):
```yaml
#cloud-config
apt:
  primary:
    - arches: [default]
      uri: http://de.archive.ubuntu.com/ubuntu/
```

**2. Add SSH Keys** (secure alternative to project-level keys):
```yaml
#cloud-config
users:
  - name: admin
    ssh_authorized_keys:
      - ssh-rsa AAAAB3NzaC1yc2E... user@laptop
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
```

**3. Install Docker:**
```yaml
#cloud-config
packages:
  - docker.io
  - docker-compose

runcmd:
  - systemctl enable docker
  - systemctl start docker
```

**4. Security Hardening:**
```yaml
#cloud-config
packages:
  - fail2ban
  - ufw

runcmd:
  - ufw default deny incoming
  - ufw default allow outgoing
  - ufw allow 22/tcp
  - ufw --force enable
  - systemctl enable fail2ban
  - systemctl start fail2ban
```

**5. Combined Configuration:**
```yaml
#cloud-config
apt:
  primary:
    - arches: [default]
      uri: http://mirror.example.com/ubuntu/

users:
  - name: admin
    ssh_authorized_keys:
      - ssh-rsa AAAAB3... admin@company
    sudo: ALL=(ALL) NOPASSWD:ALL

packages:
  - fail2ban
  - ufw
  - docker.io

runcmd:
  - ufw allow 22/tcp
  - ufw --force enable
  - systemctl enable docker fail2ban
```

**Important Notes:**

- YAML must be valid syntax (use a YAML validator if unsure)
- If you don't start with `#cloud-config`, the module will add it automatically
- Cloud-init runs ONCE on first boot only
- Invalid YAML will cause provisioning to fail silently
- Maximum size: 32 KiB (Hetzner API limit)
- Documentation: https://docs.hetzner.cloud/#servers-create-a-server

## Screenshots
![Screenshot 1](screenshots/1.png)
![Screenshot 2](screenshots/2.png)
![Screenshot 3](screenshots/3.png)
![Screenshot 4](screenshots/4.png)
![Screenshot 5](screenshots/5.png)
![Screenshot 6](screenshots/6.png)
![Screenshot 7](screenshots/7.png)
![Screenshot 8](screenshots/8.png)
![Screenshot 9](screenshots/9.png)
![Screenshot 10](screenshots/10.png)

## Troubleshooting

**API Connection Failed**: Check API token in server group settings **Server Not Found**: Verify VPS ID custom field exists **Floating IP Issues**: Enable in configurable options

Enable module debugging: Configuration → System Logs → Module Log

## Support

This module is free and open source, and we maintain it in our spare time. We hope it makes running Hetzner Cloud on WHMCS a little easier for you.

If something isn't working as expected:

1. Check the module log first (Configuration → System Logs → Module Log). It usually points straight to the cause.
2. Open an issue with what you saw, the steps that led to it, and the relevant log output. The more detail you share, the quicker we can help.

We can't promise a response time, but we read every report and genuinely appreciate bug reports, ideas, and pull requests. Contributions are always welcome.

## License

MIT License - see [LICENSE](LICENSE) file for details.

Free and open source. Use it, fork it, improve it.

## Other ArkHost Modules

Check out our other WHMCS modules at [arkhost.com/whmcs-modules.php](https://arkhost.com/whmcs-modules.php "https://arkhost.com/whmcs-modules.php")

## Changelog

### v1.2.5
**Fixed:**
- Restored server provisioning after Hetzner removed the deprecated `datacenter` request property on July 1, 2026
- Replaced datacenter discovery with the Hetzner Locations API
- Updated client-area location display for the current server response format
- Added automatic conversion of legacy datacenter values such as `fsn1-dc14` to `fsn1`

### v1.2.4
**Fixed:**
- IP address now stored on the service at provisioning (and backfilled for existing services) so it displays in WHMCS instead of showing blank
- Service username now set to `root` to match the actual VPS login
- Install password now actually removed after the 72-hour window (was staying visible via the eye icon, including for older services that predated expiry tracking)

### v1.2.3
**Added:**
- VNC console key combination bar with Ctrl+Alt+Del, modifier keys (Shift, Alt, Ctrl), Tab, Enter, Del, Backspace, Esc, Shift+Up, and Fullscreen toggle

### v1.2.2
- WHMCS v9 compatibility: Updated Smarty template syntax for Smarty v4

### v1.2.1
**Added:**
- Shutdown button functionality (graceful server shutdown)
- Enhanced backup permission detection (automatically detects Hetzner-enabled backups)
- Improved Actions sidebar with custom Font Awesome icons
- VNC console opens in new tab
- hooks.php for cleaner UI (hides default WHMCS elements)

**Fixed:**
- Backup creation/deletion API response format
- Shutdown action now properly whitelisted in ClientAreaAPI

### v1.2.0
**Added:**
- Cloud-init support (optional per-product YAML configuration)
- CX Gen3 server types: CX23, CX33, CX43, CX53
- OS images: Debian 13, AlmaLinux 10, Rocky 10, CentOS Stream 10, openSUSE 15, Fedora 41/42
- Password expiration (72-hour visibility window)

**Fixed:**
- API pagination - server types and OS images now show all available options (per_page=50)
- Password reset timestamp tracking
- Password display in client area after reset

**Documentation:**
- Cloud-init configuration examples
- Updated server types and OS lists

### v1.1.1
**Fixed:**
- Firewall rules now accept source 0.0.0.0/0 for all IPs
- "ANY" protocol creates both TCP and UDP rules automatically
- Outbound firewall rules now use correct destination_ips field

**Added:**
- Incoming/Outgoing direction support for firewall rules
- Proper CIDR notation validation (supports /0, /4, /8, /16, etc.)

**Improved:**
- Removed description/note field from firewall interface
- Changed "Source/Destination" column to "IP/CIDR" for clarity
- Translated all firewall column headers to all supported languages
- Cleaned up unused language strings

### v1.1
**Fixed:**
- Firewall Display: Removed fake "default open" rules that didn't exist in Hetzner dashboard
- Firewall Creation: Fixed "name already used" error with unique timestamp naming
- Firewall Association: Fixed API errors when attaching firewalls to servers
- API Compatibility: Corrected data formats for Hetzner API (ports as strings, proper server object structure)

### v1.0
- Initial release

© 2025 ArkHost
