Install Hakopod
Prepare a dedicated Linux server, choose its network settings and review the installation plan.
Install 0.1.0-alpha.58, a development release, on a fresh dedicated Linux server. Prebuilt archives are available for AMD64 and ARM64. The installer sets up K3s, the management API and dashboard, with either managed PostgreSQL or an existing dedicated database. You choose the first administrator in the setup screen; there is no default account or password.
Before you begin#
Use Ubuntu 24.04/26.04 or Debian 12/13, with systemd, cgroup v2 and swap disabled. Start with at least 4 GiB RAM and 30 GiB free disk, plus capacity for application pods and rollouts. Runtime caps are configurable limits, not measured idle memory use.
Missing Bash, Python 3.10+, curl, CA certificates and other distro prerequisites are installed from the server's existing package repositories. Hakopod uses prebuilt release files, so you do not need a compiler or Node on the server. If curl is absent, download the script on another machine, transfer it and run it with the server's existing POSIX shell.
Prepare an application domain, a reachable IPv4 address for the server, and the required DNS and NAT routes. Choose dashboard access through an SSH tunnel, or a separate HTTPS origin with automatic Let's Encrypt certificates or your verified PEM certificate and key. Storage is optional; enable it if your applications need persistent volumes.
Use a fresh host. The installer stops if it finds an existing Kubernetes installation, conflicting ports or installation paths it does not own. The installer does not adopt another cluster or change your firewall, DNS, SSH, swap or package repositories.
Run the installer#
From an account with sudo access:
curl -fsSL https://hakopod.com/scripts/installer.sh | sudo sh
Already running as root:
curl -fsSL https://hakopod.com/scripts/installer.sh | sh
The installer asks for settings in your terminal, prints its plan and asks you to type install. It downloads the release files for your architecture, checks them against the SHA256 manifest, rejects unsafe archive entries and removes temporary downloads when it exits. Missing prerequisite packages may be installed before the configuration review.
To inspect the bootstrap first, save it locally and read it before running:
curl -fsSL https://hakopod.com/scripts/installer.sh -o installer.sh &&
less installer.sh &&
sh installer.sh --help &&
sudo sh installer.sh
For a repeatable install, prepare an explicit JSON configuration using the tagged installer guide and example. Replace its documentation-only addresses and review every setting. A noninteractive run requires --config with --dry-run or --yes; the configuration version must match the release.
sh installer.sh --version 0.1.0-alpha.58 --config install.json --dry-run
sudo sh installer.sh --version 0.1.0-alpha.58 --config install.json
Checksums show whether files match the release manifest. They do not identify the publisher. Release assets and provenance attestations support an independent gh attestation verify FILE --repo hakopod/hakopod check on an operator machine.
Upgrade an existing installation#
Check the installed version with readlink /opt/hakopod/current, then review the release notes for supported source versions. Keep a verified backup. Each upgrade briefly restarts the management API and dashboard; application containers keep running.
curl --fail --location --proto '=https' --tlsv1.2 \
https://hakopod.com/scripts/installer.sh -o installer.sh &&
sudo sh installer.sh --upgrade --version 0.1.0-alpha.58
Keep && between downloading and running the script: if the download fails, the upgrade command will not execute an older local file. If the website is unavailable, use the installer asset from this exact GitHub release as the download URL.
Older installations may need intermediate releases. For example, alpha.34 can upgrade to alpha.36, then alpha.38. Run each supported step with --upgrade --version VERSION, and stop if any step fails. Never use --resume or edit the saved configuration to force an upgrade. The script asks you to confirm each step before stopping management services.
Answer the installer prompts#
For your first install, choose self-hosted, ssh dashboard access and managed PostgreSQL. You can use applications over public HTTPS while keeping the administrator dashboard behind SSH. The current installer also supports automatic dashboard certificates from Let's Encrypt. Older kits may require a supplied dashboard certificate; see the availability note below.
The examples below use application domain apps.example.com, dashboard hostname console.example.com and public IP 203.0.113.10. Replace these documentation-only values with your own domain and server addresses.
| Prompt | What to enter |
|---|---|
| Deployment mode | self-hosted for your own installation, including on AWS, Azure, GCP or a VPS provider. The provider does not determine the Hakopod deployment mode. |
| Operator-owned application DNS domain | A domain you control, such as apps.example.com; no scheme, port or wildcard prefix. Do not leave this blank. |
| Server IPv4 address | An IPv4 address actually assigned to the VM's network interface. On AWS, Azure and GCP this is normally its private NIC IP, not the public NAT IP. |
| Kubernetes node name | Accept hakopod-server, or use a unique DNS-compatible node name. |
| Worker-reachable K3s DNS name or IPv4 | Accept the private NIC IP when future workers can reach it over the VNet or private network. Otherwise configure a supervisor endpoint that the workers can reach. |
| Dashboard access | ssh for a private tunnel, or https for a separate dashboard hostname. See the certificate choices and availability below. |
| Loopback dashboard port | Accept 3000 for SSH mode. Do not expose this port publicly. |
| Application certificates | production when public application DNS and TCP 80 are ready; staging for ACME testing; off to skip application certificate automation. |
| ACME account contact email | Your certificate contact address. This does not create a login account. Also required for automatic dashboard certificates, even when application certificates are off. |
| Public TCP ports | Leave blank for ordinary HTTP apps and private databases. Enter 5432,5433,6379 only if you intend to provision those public ingress ports. This does not enable or publish the management database automatically. |
| PostgreSQL | Accept managed for a new installer-owned database. Existing local/external options are explained below. |
| Also install Docker Engine | Accept false; K3s already has containerd. Choose true only if you separately need Docker. |
| Node-local application volumes | true if templates such as Redis or databases need local persistent volumes. Local storage is tied to this node and is not a backup. |
| K3s / API / dashboard / PostgreSQL memory caps | Start with 2048, 256, 320, 256 MiB respectively. Preflight checks the total configuration against available RAM. |
| Maximum pods | Accept 50, or use 20 for the smaller pod limit. This is a limit, not reserved capacity. |
| Configure Google / GitHub / GitLab login now | N to start with password login; Y only when you have that provider's client configuration. |
| Final installation confirmation | Read the plan and type install only when its addresses, ports and storage choices are correct. |
On the VM, inspect its assigned IPv4 addresses before answering:
ip -4 -brief address
ip -4 route get 1.1.1.1
The route's src address identifies the outbound NIC address. Do not use loopback, a container bridge, or a public IP absent from the interface list. On AWS, Azure and GCP, public IPv4 is normally mapped to the NIC by the provider; DNS uses that public address while K3s and the direct HTTPS listener bind the private address. On a VPS with public IPv4 assigned directly inside the guest, the public address can be a valid node IP; prefer a configured private interface when workers communicate privately.
Create wildcard application DNS: *.apps.example.com → 203.0.113.10. Allow inbound TCP 80/443 through the provider firewall and host firewall for application traffic. Keep SSH and supervisor/node traffic restricted to the relevant administrators or nodes. The installer does not create these DNS or firewall rules.
Prepare your cloud provider#
Use this section to prepare networking. Hakopod's host acceptance tests do not cover every provider, image or instance family listed here. Choose a fresh supported Ubuntu 24.04/26.04 or Debian 12/13 image, AMD64 or ARM64, at least 4 GiB RAM and 30 GiB free disk. An advertised 30 GiB disk will have less than 30 GiB free after the OS is installed. Provider-specific images such as Amazon Linux, Oracle Linux or Container-Optimized OS are not substitutes for a supported distro.
Reserve a stable public IPv4 address before configuring DNS. Put the server and future workers on a mutually reachable private network, with address ranges that do not overlap Hakopod's fixed 10.42.0.0/16 pod or 10.43.0.0/16 service networks. Peering or a VPN needs routing and firewall configuration. Matching private IP ranges in separate providers do not connect those networks. Private-only servers need a separately planned inbound path for public applications. An outbound NAT gateway alone does not provide that path.
AWS EC2#
- Launch a supported Ubuntu or Debian AMI with the chosen architecture. For ARM64/Graviton, select an ARM64 AMI. Size its EBS root volume for the required free space and your application data.
- Use the instance's primary private IPv4 on its network interface for the node IP. Use that address, or private DNS resolving to it, for workers in the VPC. An Elastic IP or auto-assigned public IP is normally not present in the guest's interface list and must not be entered as the node IP.
- Associate an Elastic IP for stable public DNS. A directly public instance needs an appropriate subnet route to an Internet Gateway. Do not add a NAT Gateway to solve inbound HTTP access.
- Apply the port rules below to the instance's security group. Check subnet network ACLs and the host firewall as well. Restrict worker rules to the worker security group or actual private node addresses where applicable.
- Point application DNS at the Elastic IP. Add a separate dashboard DNS record only for direct HTTPS mode. SSH with the username supplied by the selected AMI and your key; Ubuntu images commonly use
ubuntu, while Debian image usernames can differ.
See EC2 IP addressing. AWS RDS is optional: choose external PostgreSQL only after preparing the dedicated database, network access and verified TLS configuration described below. EC2 hosting does not automatically grant applications an IAM role.
Microsoft Azure#
- Create a supported Ubuntu or Debian VM with enough memory and OS/data disk space. Use the administrator username you selected when creating it.
- Find the private IP under the VM's network interface IP configuration and confirm it appears in
ip -4 -brief address. Enter this as the node IP. Keep it stable through Azure's NIC IP allocation settings. The VM's public IP is for DNS and remote access, not the node-IP prompt. - Associate a stable Public IP resource with the NIC for direct public ingress. Use the private NIC IP or private DNS for workers in the same VNet, or a deliberately routed peered network.
- Configure inbound rules in the network security group. If NSGs apply to both the subnet and NIC, check both, including rule priorities. Check the guest firewall too.
- Point wildcard application DNS at the Public IP resource. For direct HTTPS, point a sibling dashboard hostname at the same public IP and permit its configured port from administrator addresses.
See Azure network interface IP addresses. Azure Database for PostgreSQL is an optional external database; prepare its database, credentials, network access and TLS trust separately. The installer does not create it.
Google Cloud Compute Engine#
- Create a VM from a supported Ubuntu or Debian image with matching AMD64/ARM64 architecture and enough persistent disk space. Use your configured SSH or OS Login identity; do not assume every image has an
ubuntuaccount. - Enter the NIC's internal IPv4 as the node IP, and confirm it inside the guest. Use internal IP or private DNS for workers on the reachable VPC network. The external IPv4 address is normally translated by Google Cloud and is not an address to bind inside the VM.
- Reserve and associate a static external IPv4 address for DNS. A VM without external ingress will not become publicly reachable simply by enabling Cloud NAT; Cloud NAT supports outbound connections.
- Create the needed VPC firewall rules, targeting the VM's applicable network tags or service account. Check applicable firewall policies and the guest firewall. The console's Allow HTTP/HTTPS choices do not configure private K3s worker traffic.
- Point application DNS at the static external address. For direct HTTPS dashboard access, also configure its separate hostname, certificate and administrator-restricted port.
See GCP static external IP addresses. Cloud SQL for PostgreSQL is an optional external database. Its connection method must match the installer's TCP and verify-full requirements; the installer does not install a Cloud SQL Auth Proxy or configure IAM database authentication.
Other VPS and dedicated-server providers#
Use the addresses shown by the VM's interface check. Do not enter a floating, reserved or NAT address unless it is assigned inside the VM. Provider-managed firewalls supplement the server's own firewall.
| Provider | Node and worker networking | Public access and firewall |
|---|---|---|
| DigitalOcean | Use the configured VPC interface for private workers, or an assigned public interface for a single node. Confirm the address in the guest. | Point DNS at the chosen stable public endpoint. Apply Cloud Firewall rules to the Droplet; a Reserved IP is not automatically the node's bind address. |
| Hetzner Cloud | Attach a private Network when workers need private connectivity; use its assigned NIC address. | Use the public Primary IP for direct ingress and Cloud Firewall rules. Floating IPs need their own routing/guest configuration and should not be copied blindly into the node-IP prompt. |
| Vultr | Use an attached VPC interface for private workers; confirm that every node can route to the server. | Use the instance's stable public endpoint for DNS and apply its attached Firewall Group. |
| Akamai / Linode | Use the configured VPC or private interface when available; confirm the guest address and routing for the selected interface setup. | Use the assigned public endpoint and the relevant Cloud Firewall. Private-address availability does not establish connectivity to another region or provider. |
| UpCloud | Use the assigned interface on a configured private SDN network for workers. | Configure public ingress to the server's public endpoint and review both provider firewall settings and the guest firewall. |
| OVHcloud | Use a configured private network/vRack where the product supports it, or a directly assigned interface address. | Verify routing for the selected VPS, Public Cloud or dedicated-server product; firewall controls differ. Additional/failover IPs can require guest configuration. |
| Oracle Cloud Infrastructure | Use the VNIC's private IPv4 for the node and a routed VCN network for workers. Choose a supported Ubuntu image, not Oracle Linux. | Associate a reserved public IP for stable DNS. Check applicable NSGs, subnet security lists and the guest firewall. |
| Scaleway | Use a configured Private Network interface for private workers and verify its guest address. | Associate the appropriate public/Flexible IP for DNS; apply the instance security group and guest firewall rules. |
| Bare metal, another provider or a home lab | Choose a real NIC address reachable by workers. Establish private routing before enrollment. | Use directly assigned public IPv4 or deliberate router port forwarding. Carrier-grade NAT may prevent inbound access; IPv6-only installation is not supported by this installer. |
Apply the same port rules on every provider#
| Port | Allowed source and purpose |
|---|---|
| TCP 22, or your existing SSH port | Administrator addresses, VPN or bastion; use your existing SSH access policy. |
| TCP 80 and 443 | Public application clients; production HTTP-01 certificate validation also needs inbound 80. |
| TCP 8443, if selected | Intended administrators only, for the direct HTTPS dashboard. Use your selected port if different. |
| TCP 3000 and 8080 | Keep private: the default SSH dashboard and management API bind loopback. Use SSH forwarding for remote access. |
| TCP 6443 | Enrolled workers and relevant administrators to the K3s server; prefer private networking. |
| TCP 10250 | Cluster nodes only. |
| UDP 8472 | Cluster nodes only for Flannel VXLAN; never expose this publicly. |
| Additional configured TCP ports | Only the clients and source networks intended for those published services. Omit unused mappings. |
Allow the server outbound DNS, HTTPS downloads and access to its configured database or other dependencies. Bootstrap downloads, package installation, registry pulls and ACME require working outbound connectivity. Wildcard application DNS is an A record for *.apps.example.com pointing at the stable public IPv4; it does not issue a wildcard certificate or make private network routes reachable.
Open the SSH dashboard#
After installation, run the tunnel on your laptop, not on the server:
ssh -N -L 3000:127.0.0.1:3000 YOUR_VM_USER@YOUR_PUBLIC_IP
If SSH uses a key, add -i /path/to/private-key. Keep the terminal open and visit http://localhost:3000. The public VM IP on port 3000 is not the SSH-mode dashboard URL.
Automatic dashboard HTTPS with Let's Encrypt#
Availability: 0.1.0-alpha.58 includes automatic dashboard HTTPS setup. Successful public certificate issuance depends on your DNS and network access; release install/upgrade checks do not establish that a particular domain will validate. SSH access and supplied certificates remain available.
In the installer, select https, then accept the default certificate choice letsencrypt. You enter the dashboard origin and a contact email; no certificate or private-key paths are required.
| Prompt | Example |
|---|---|
| Dashboard access | https |
| Dashboard certificate | letsencrypt |
| Dashboard HTTPS port | 8443 |
| Dashboard origin | https://console.example.com:8443, outside the application domain |
| ACME contact email | Your certificate contact email |
Point console.example.com at the server's public IP. Keep inbound TCP 80 available for certificate issuance and future renewal, and permit 8443 from the administrators who need dashboard access. Do not enter a wildcard hostname or an IP address as the dashboard origin.
The installer reuses its pinned cert-manager module and the existing HAProxy HTTP-01 ingress. Applications keep ports 80/443; the dashboard keeps its separate HTTPS port. Dashboard issuance uses production Let's Encrypt even when application certificates are staging or off. Installation waits for a valid, trusted certificate before reporting success. If issuance fails, inspect dashboard DNS, TCP 80 and cert-manager challenges, fix the problem and resume using the saved configuration.
cert-manager renews the certificate automatically. A host timer checks for a renewed certificate and key every six hours and after boot. It validates the hostname, trust chain, expiry and key before installing the new certificate and key and briefly restarting the dashboard. Applications are not restarted. If validation or restart fails, the previous certificate and key stay in place when available. Leave dashboard DNS and the HTTP-01 route in place for future renewals.
Check the renewal timer and its recent logs on the server:
sudo systemctl status hakopod-dashboard-certificate.timer
sudo journalctl -u hakopod-dashboard-certificate.service --no-pager -n 30
Use a supplied HTTPS dashboard certificate#
Use a separate hostname outside the application domain. For apps.example.com, console.example.com is valid; console.apps.example.com is not. Create its DNS record pointing at the public server IP, and allow the configured dashboard port only from intended administrator addresses.
| HTTPS-only prompt | Example |
|---|---|
| Dashboard HTTPS port | 8443; application ingress owns 443. |
| Dashboard origin | https://console.example.com:8443 with the scheme and matching port; no trailing slash or path. |
| PEM certificate chain | /root/hakopod-dashboard.crt, an existing regular file containing the valid certificate chain for that hostname. |
| PEM private key | /root/hakopod-dashboard.key, the matching existing key, with mode 0600 or 0400. |
Choose provided when you already manage your own certificate. Obtain the certificate before selecting this option. A self-signed certificate is insufficient for the installer's system-trust readiness check. Certificate files must be regular files, not symlinks. For an existing Certbot certificate, copy its files into the paths you will enter:
sudo install -m 0644 /etc/letsencrypt/live/console.example.com/fullchain.pem /root/hakopod-dashboard.crt
sudo install -m 0600 /etc/letsencrypt/live/console.example.com/privkey.pem /root/hakopod-dashboard.key
These commands copy an existing certificate; they do not issue one. The installer copies it into /etc/hakopod. You remain responsible for renewing those installed copies and restarting the dashboard; renewing only the original Certbot files does not refresh the installed dashboard certificate.
Fix common setup errors#
- HTTPS mode requires a DNS origin with the configured dashboard_port: the origin was empty or its port differed. With port 8443, enter
https://console.example.com:8443; then choose automatic Let's Encrypt in the updated installer, or supply both certificate files in provided mode. With older kits, choose SSH if you do not have a certificate. - node_ip is not assigned to a local interface: use the VM's private NIC address on AWS, Azure or GCP, or another address confirmed in the guest's interface list. Keep provider public NAT addresses in DNS and your SSH command.
- Dashboard must have a separate origin outside the application domain: use a sibling hostname, or a different domain, for the dashboard.
- Unbound PersistentVolumeClaims after deploying a template: enable node-local application volumes when you need that storage, or configure a supported storage provider before deploying persistent workloads.
Choose PostgreSQL#
- Managed, the default: one installer-owned PostgreSQL 17 pod in the new K3s cluster. No PostgreSQL server package is installed on the host.
- Local: your existing server over loopback TCP, with an explicit password. Unix sockets and OS peer authentication are not supported.
- External: your existing remote server with
sslmode=verify-full. Its certificate must match the connection hostname. Supply a private/RDS CA file or use system trust roots.
Existing database modes support PostgreSQL 14–18 and install only missing client tools. Prepare a dedicated empty database owned by its connection role. Checks require a writable primary, CONNECT, public-schema USAGE/CREATE, the public default schema, and an empty public schema. System databases and unrelated application schemas are refused.
Save the connection URI in a root-owned file with mode 0600 or 0400. Interactive setup can accept it as hidden input. Add these paths to the normal installer JSON for an external database:
{
"database_mode": "external",
"database_url_file": "/root/hakopod-database-url",
"database_ca_file": "/root/rds-ca-bundle.pem"
}
This is a configuration fragment. Keep passwords and connection URIs out of JSON and shell commands. The installer does not create or delete an existing database or role, provision RDS, rotate IAM tokens, manage replicas, or migrate existing schemas. Use a compatible pg_dump and complete a restore drill before enabling management backups for an existing server.
Claim your installation#
The default dashboard listens on localhost port 3000 and the API on localhost port 8080. Open the printed SSH tunnel, then use the exact localhost origin. Direct HTTPS uses the origin and certificate reviewed during setup.
After readiness checks, the installer explains how to privately read /etc/hakopod/secrets/setup-token. Enter it in the dashboard and choose your name, email and password. The ACME contact email is not your administrator identity. Completing setup closes the first-owner claim; keep the token private afterwards too.
Self-hosted signup is invitation-only after setup. Changing a signup flag does not open public registration in this mode. Team invitations are included in Free. Email delivery and OAuth need separate provider configuration; the installer can collect optional Google, GitHub and GitLab OAuth settings. See Accounts and access.
DNS, TLS and ports#
Allow application HTTP/HTTPS on TCP 80/443. Restrict Kubernetes supervisor and node traffic to administrators and enrolled nodes; keep Flannel VXLAN private.
Let's Encrypt HTTP-01 can secure individual application names once public DNS and port 80 are reachable. Staging certificates are not browser-trusted. Wildcard DNS-01 automation is not installed. Supplied dashboard certificates have a separate renewal responsibility.
Additional public TCP ports remain self-hosted only. An administrator provisions them before an application can request a mapping. See Networking.
Recovery and verification#
Preserve /etc/hakopod, its authentication encryption key, the original installer configuration and artifacts, consistent PostgreSQL backups, K3s state and application volumes. --resume requires the original configuration and artifact bytes; it is not an upgrade command. Adding workers does not make the first database or ingress highly available.
The release publishes native-host acceptance reports alongside its archives. They distinguish managed installs on AMD64/ARM64 from existing local and verified-TLS PostgreSQL fixtures on AMD64. They do not establish host reboot recovery, public ACME issuance, physical AWS RDS acceptance, every supported distribution, or existing-database modes on ARM64. Test recovery on your own disposable host before production use. See Backups for dashboard-managed backup scope.