Run a persistent Apus Gateway on Oracle Cloud's Always Free ARM tier (up to 4 OCPU, 24 GB RAM, 200 GB storage) at no cost.

Prerequisites

Setup

Create an OCI instance

1. Log into [Oracle Cloud Console](https://cloud.oracle.com/).
2. Navigate to **Compute > Instances > Create Instance**.
3. Configure:
   - **Name:** `apus`
   - **Image:** Ubuntu 24.04 (aarch64)
   - **Shape:** `VM.Standard.A1.Flex` (Ampere ARM)
   - **OCPUs:** 2 (or up to 4)
   - **Memory:** 12 GB (or up to 24 GB)
   - **Boot volume:** 50 GB (up to 200 GB free)
   - **SSH key:** Add your public key
4. Click **Create** and note the public IP address.
If instance creation fails with "Out of capacity", try a different availability domain or retry later. Free tier capacity is limited.

Connect and update the system

```bash
ssh ubuntu@YOUR_PUBLIC_IP

sudo apt update && sudo apt upgrade -y
sudo apt install -y build-essential
```

`build-essential` is required for ARM compilation of some dependencies.

Configure user and hostname

```bash
sudo hostnamectl set-hostname apus
sudo passwd ubuntu
sudo loginctl enable-linger ubuntu
```

Enabling linger keeps user services running after logout.

Install Tailscale

```bash
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --ssh --hostname=apus
```

From now on, connect via Tailscale: `ssh ubuntu@apus`.

Install Apus

```bash
curl -fsSL https://ami-docs.dev.apuscorp.com/install.sh | bash
source ~/.bashrc
```

When the installer offers to hatch an agent, defer it — the gateway steps
below configure the host first.

Configure the gateway

Use token auth with Tailscale Serve for secure remote access.

```bash
apus config set gateway.bind loopback
apus config set gateway.auth.mode token
apus doctor --generate-gateway-token
apus config set gateway.tailscale.mode serve
apus config set gateway.trustedProxies '["127.0.0.1"]'

apus gateway install
systemctl --user restart apus-gateway.service
```

`gateway.trustedProxies=["127.0.0.1"]` here is only for the local Tailscale Serve proxy's forwarded-IP/local-client handling. It is **not** `gateway.auth.mode: "trusted-proxy"`. Diff viewer routes keep fail-closed behavior in this setup: raw `127.0.0.1` viewer requests without forwarded proxy headers return `Diff not found`. Use `mode=file` / `mode=both` for attachments, or intentionally enable remote viewers and set `plugins.entries.diffs.config.viewerBaseUrl` (or pass a proxy `baseUrl`) if you need shareable viewer links.

Lock down VCN security

Block all traffic except Tailscale at the network edge:

1. Go to **Networking > Virtual Cloud Networks** in the OCI Console.
2. Click your VCN, then **Security Lists > Default Security List**.
3. **Remove** all ingress rules except `0.0.0.0/0 UDP 41641` (Tailscale).
4. Keep default egress rules (allow all outbound).

This blocks SSH on port 22, HTTP, HTTPS, and everything else at the network edge. You can only connect via Tailscale from this point on.

Verify

```bash
apus --version
systemctl --user status apus-gateway.service
tailscale serve status
curl http://localhost:18789
```

Access the Control UI from any device on your tailnet:

```
https://apus.<tailnet-name>.ts.net/
```

Replace `<tailnet-name>` with your tailnet name (visible in `tailscale status`).

Verify the security posture

With the VCN locked down (only UDP 41641 open) and the Gateway bound to loopback, public traffic is blocked at the network edge and admin access is tailnet-only. That removes the need for several traditional VPS hardening steps:

Traditional step Needed? Why
UFW firewall No The VCN blocks traffic before it reaches the instance.
fail2ban No Port 22 is blocked at the VCN; no brute-force surface.
sshd hardening No Tailscale SSH does not use sshd.
Disable root login No Tailscale authenticates by tailnet identity, not system users.
SSH key-only auth No Same -- tailnet identity replaces system SSH keys.
IPv6 hardening Usually not Depends on VCN/subnet settings; verify what is actually assigned/exposed.

Still recommended:

Quick verification commands:

# Confirm no public ports are listening
sudo ss -tlnp | grep -v '127.0.0.1\|::1'

# Verify Tailscale SSH is active
tailscale status | grep -q 'offers: ssh' && echo "Tailscale SSH active"

# Optional: disable sshd entirely once Tailscale SSH is confirmed working
sudo systemctl disable --now ssh

ARM notes

The Always Free tier is ARM (aarch64). Most Apus features work fine; a small number of native binaries need ARM builds:

Verify the architecture with uname -m (should print aarch64). For binaries without an ARM build, install from source or skip them.

Persistence and backups

Apus state lives under:

These survive reboots. To create a backup archive:

apus backup create
apus backup restore <archive.tar.gz> --target <fresh-directory>

Absolute symbolic links keep their original target locations, including links to separately backed-up config or credentials. Review these links before activating state on another host or at another path; see the backup symbolic-link caveat. Restore verifies and extracts into a fresh staging directory; activation is a separate offline step. See Restore a full archive for the rollback warnings and activation sequence.

Fallback: SSH tunnel

If Tailscale Serve is not working, use an SSH tunnel from your local machine:

ssh -L 18789:127.0.0.1:18789 ubuntu@apus

Then open http://localhost:18789.

Troubleshooting

Instance creation fails ("Out of capacity") -- Free tier ARM instances are popular. Try a different availability domain or retry during off-peak hours.

Tailscale will not connect -- Run sudo tailscale up --ssh --hostname=apus --reset to re-authenticate.

Gateway will not start -- Run apus doctor --non-interactive and check logs with journalctl --user -u apus-gateway.service -n 50.

ARM binary issues -- Most npm packages work on ARM64. For native binaries, look for linux-arm64 or aarch64 releases. Verify architecture with uname -m.

Next steps

Related