NOTE (2026-06-07): This post describes the initial Hetzner Cloud setup. Hetzner was subsequently replaced with a no-KYC privacy VPS (Servury) because Hetzner requires KYC/AML identity verification. The current process is documented in tutorial-privacy-vps-ario-gateway.md.
archerships.com is deployed to the Arweave permanent web. Content lives on chain permanently – but serving it to browsers requires a gateway, and the default gateway (arweave.net) runs on CDN77 infrastructure that has periodic multi-hour outages. When CDN77 goes down, the site returns HTTP 572 and is unreachable.
The fix is a self-hosted ar.io gateway node. The node caches your content locally, falls back to arweave.net on cache miss, and serves requests from your own server. Your site stays up even when arweave.net is having a bad day.
This guide uses Hetzner Cloud’s CAX21 ARM instance (~$5/month). If you prefer Oracle Cloud Always Free, the setup is identical after the instance is running – see the companion Oracle provisioning guide.
Architecture
Browser
|
v
Cloudflare (DNS proxy + CDN edge)
|
v
Hetzner CAX21 -- nginx (TLS termination)
|
v
ar.io node (Docker, localhost:3000)
- serves content from local cache
- fetches from arweave.net on cache miss
- resolves ARNS names (e.g. archerships -> manifest txId)
1. Provision the Server
1.1 Create a Hetzner account
hetzner.com/cloud – payment by card or PayPal. For XMR payment see the companion essay on Monero-accepting VPS providers; most of those providers support the same Docker-based setup below.
1.2 Install the hcloud CLI
brew install hcloud1.3 Create a project and API token
- console.hetzner.cloud > New project > name it (e.g. archerships)
- Project > Security > API Tokens > Generate API Token (Read & Write)
- Copy the token – it is shown only once
1.4 Configure the CLI
hcloud context create archerships
# Paste your API token when prompted1.5 Add your SSH key
hcloud ssh-key create --name macbook --public-key-from-file ~/.ssh/id_ed25519.pub1.6 Create the server
hcloud server create \
--name ar-io-gateway \
--type cax21 \
--image ubuntu-24.04 \
--ssh-key macbook \
--location ashcax21 is the ARM instance: 4 vCPU, 8 GB RAM, 80 GB SSD. Cost: ~$5/month. Location options: ash (Ashburn VA), fsn1 (Falkenstein DE), hel1 (Helsinki FI).
The command prints the server’s public IP. Note it.
1.7 Open firewall ports
hcloud firewall create --name gateway-fw
hcloud firewall add-rule gateway-fw --protocol tcp --port 22 --source-ips 0.0.0.0/0 --direction in
hcloud firewall add-rule gateway-fw --protocol tcp --port 80 --source-ips 0.0.0.0/0 --direction in
hcloud firewall add-rule gateway-fw --protocol tcp --port 443 --source-ips 0.0.0.0/0 --direction in
hcloud firewall apply-to-resource gateway-fw --type server --server ar-io-gateway2. Server Setup
ssh root@<server-ip>2.1 System update and packages
apt update -y && apt upgrade -y
apt install -y curl git nginx sqlite3 build-essential ufw2.2 Firewall (ufw)
ufw allow 22 && ufw allow 80 && ufw allow 443 && ufw enable2.3 Docker
apt-get install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-pluginVerify: docker run hello-world
3. DNS Configuration
In Cloudflare for your domain:
- Type: A, Name: @, Content:
<server-ip>, Proxy: ON (orange cloud) - Type: A, Name: *, Content:
<server-ip>, Proxy: ON
The wildcard (*) record is needed for ARNS subdomain resolution (e.g. archerships.yourdomain.com).
Wait 2 minutes, then verify:
curl -sI http://yourdomain.com | head -34. TLS Certificates
Using a Cloudflare Origin Certificate (15-year validity, no renewal needed):
- Cloudflare > SSL/TLS > Origin Server > Create Certificate
- Hostnames:
yourdomain.com,*.yourdomain.com - Key type: RSA 2048, validity: 15 years
- Download the certificate and private key
On the server:
mkdir -p /etc/ssl/yourdomain
nano /etc/ssl/yourdomain/cert.pem # paste certificate
nano /etc/ssl/yourdomain/key.pem # paste private key
chmod 600 /etc/ssl/yourdomain/key.pemSet Cloudflare SSL/TLS mode to “Full (strict)”.
5. ar.io Node
5.1 Clone the repository
cd /var/lib
git clone -b main https://github.com/ar-io/ar-io-node ar-io-node
cd ar-io-node5.2 Configure .env
nano .envPaste (replace yourdomain.com with your actual domain):
ARNS_ROOT_HOST=yourdomain.com
GRAPHQL_HOST=arweave.net
GRAPHQL_PORT=443
START_HEIGHT=1580000
TRUSTED_GATEWAYS_URLS={"https://arweave.net": 1}
START_HEIGHT skips syncing all 1.6M+ historical Arweave blocks. The node fetches and caches content on first request, falling back to arweave.net on cache miss. A high START_HEIGHT means the node cannot join the ar.io network as a verified gateway – that is fine for personal use.
5.3 Start the node
docker compose up -d
docker compose logs -f --tail=30Expected output: block sync progress, no ERROR lines. Wait until sync reports a recent block height before proceeding.
5.4 Enable auto-restart on boot
systemctl enable dockerThe compose services use restart: unless-stopped by default.
6. nginx Configuration
nano /etc/nginx/sites-available/yourdomainPaste (replace yourdomain throughout):
server {
listen 80;
listen [::]:80;
server_name yourdomain.com *.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name yourdomain.com *.yourdomain.com;
ssl_certificate /etc/ssl/yourdomain/cert.pem;
ssl_certificate_key /etc/ssl/yourdomain/key.pem;
proxy_read_timeout 120s;
proxy_connect_timeout 10s;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
}
}
Enable and reload:
ln -s /etc/nginx/sites-available/yourdomain /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && service nginx restart7. Smoke Test
# Home page
curl -sI https://yourdomain.com/ | head -5
# Expected: HTTP/2 200
# ARNS subdomain (replace "archerships" with your ARNS name)
curl -sI https://archerships.yourdomain.com/index.html | head -5The first request to a path may be slow (1-5s) while the node fetches and caches content from arweave.net. Subsequent requests are served from the local cache with no CDN77 dependency.
8. Keeping the Node Updated
ar-io-node releases approximately every few weeks:
cd /var/lib/ar-io-node
git pull origin main
docker compose pull
docker compose up -dOptional weekly cron (runs at 4am Monday):
(crontab -l 2>/dev/null; echo "0 4 * * 1 cd /var/lib/ar-io-node && git pull -q && docker compose pull -q && docker compose up -d -q 2>&1 | logger -t ar-io-update") | crontab -9. Disk Management
The node caches Arweave data in /var/lib/ar-io-node/data/. With a recent START_HEIGHT, initial growth is slow. Check periodically:
du -sh /var/lib/ar-io-node/data/
df -h /If the 80 GB SSD fills up, add a Hetzner volume via CLI:
hcloud volume create --name ar-io-data --size 100 --server ar-io-gateway --automount --format ext4Then symlink or move /var/lib/ar-io-node/data to the new mount point.
10. Rollback
To revert to arweave.net CDN (e.g. while debugging the node):
In Cloudflare, change the A record for @ back to a CNAME pointing to yourname.arweave.net. The Arweave content and ARNS record are unchanged; the switch is purely DNS and takes effect within seconds.
Hetzner vs. Oracle Always Free
Oracle’s Always Free A1 tier (4 OCPU, 24 GB RAM, 200 GB storage) has better specs than the Hetzner CAX21 and costs $0/month. The catch: Oracle Phoenix Ampere capacity is frequently exhausted (“Out of host capacity”) and can take days or weeks to open up. Hetzner CAX21 provisions in under a minute.
For a personal gateway the 8 GB RAM on Hetzner is sufficient. If you need the extra RAM headroom or want $0/month, Oracle is worth the wait.
Want to stay in touch?
- Join my Signal announce-only group to be notified when I have a new essay up and other important announcements.
- For discussion, join the libertygardeners Signal group.
- Subscribe to my mailing list.
- Email: [email protected]
- Signal: archerships.43
- Website: archerships.com
- Other social media: Substack | Twitter | Facebook | Nostr | Odysee
If you’d like to support my work:
- Share my posts.
- Become a subscriber to my newsletter.
- Attend my live events (dinner parties, conferences, pop-up cities, etc).
- Introduce me to like-minded people.
- Make a one-time donation to support my work: Crypto | Fiat
- Hire me for privacy / crypto / censorship consulting.
If there is a topic you’d like me to cover, please let me know!
Questions, comments, and suggestions are welcome.