---
name: maluos-first-server
description: Install and provision a first MaluOS Server (release 26.04.x), check its first-boot health with maluctl, get its tokens, add a local or cloud model through the gateway, and open the Business OS. Use when the user wants to set up, install, provision, or verify a new MaluOS server, or asks why a fresh MaluOS host is not healthy.
---

# Setting up a first MaluOS server

The human guide for this task is https://maluos.com/howto/first-server/ . This skill is the
same procedure written for an assistant that acts on the machine with the person.

## Where you are running

Decide this first; it changes what you can do.

- **On the MaluOS host itself** (a shell on it, or over SSH): `test -e /etc/maluos-release`
  succeeds and `maluctl` exists. You can run every command below.
- **On the person's workstation**: you can download and verify images, write cloud-init
  data, and talk the person through the install. You cannot run `maluctl` until the host
  is up and reachable over SSH.

## Preconditions

1. The target is x86-64 with at least 2 cores, 2 GB memory, 25 GB disk (8 GB and 60 GB
   recommended, plus memory for any local model). MaluOS has no arm64 build.
2. The current release number is at https://download.maluos.com/LATEST (today 26.04.20261028).
   Use it in every URL; never guess a date.
3. The person has chosen one install path:
   - **ISO** on a physical machine or VM;
   - **cloud image** (qcow2 + cloud-init) on KVM, Proxmox, OpenStack;
   - **existing Ubuntu 26.04** via the bootstrap script.

## Procedure

### 1. Download and verify (workstation)

```sh
V=$(curl -fsSL https://download.maluos.com/LATEST)
curl -fsSLO https://download.maluos.com/$V/maluos-$V-server-amd64.iso      # or ...-server-amd64.qcow2
curl -fsSLO https://download.maluos.com/$V/SHA256SUMS
curl -fsSLO https://download.maluos.com/$V/SHA256SUMS.gpg
curl -fsSL  https://apt.maluos.com/bootstrap/maluos-archive-keyring.gpg -o maluos-archive-keyring.gpg
gpg --no-default-keyring --keyring ./maluos-archive-keyring.gpg --verify SHA256SUMS.gpg
sha256sum -c SHA256SUMS --ignore-missing
```

Expected: `Good signature from "MaluOS Archive Signing Key (2026) <apt@maluos.com>"`, fingerprint
`6387 5A4A 5397 B902 E9C5 CFF8 731A DA39 4135 B9FF`, and `OK` for the file. **Stop if either
differs**; do not continue with an unverified image.

### 2a. ISO install (the person does this at the console)

- Boot the medium. Menu entries: **Install MaluOS Server** (interactive: language, keyboard,
  network, proxy, disk, user, SSH key) or **Install MaluOS Server (automated, erases first
  disk)** (creates `maluos`/`maluos`, forces a password change at first login).
- The install screens show Ubuntu naming (the stock live installer); the installed system is
  MaluOS. Tell the person this before they see it.
- Keep the network connected: the installer fetches package updates from the Ubuntu archive.

### 2b. Cloud image

Write user data that creates the person's user with an SSH key. Optionally write
`/etc/maluos/firstboot.yaml` through `write_files` to set the tenant, the admin principal or
the Business OS domain before provisioning runs. Keys the Business OS component reads:
`domain, scheme, admin_email, admin_name, admin_password, model_key, model_name,
model_provider_id, anthropic_api_key, openrouter_api_key, malumail_api_key`.

```sh
qemu-img create -f qcow2 -b maluos-$V-server-amd64.qcow2 -F qcow2 disk.qcow2 40G
qemu-system-x86_64 -enable-kvm -m 4096 -smp 2 -drive file=disk.qcow2,if=virtio \
  -drive file=seed.iso,if=virtio,format=raw -nic user,hostfwd=tcp::2222-:22
```

### 2c. Existing Ubuntu 26.04

```sh
curl -fsSL https://apt.maluos.com/bootstrap/install-maluos.sh | sudo bash   # installs maluos-server
sudo apt install maluos-api maluos-business-os                               # optional components
sudo reboot
```

This rebrands the host as MaluOS (os-release, banners, GRUB). Confirm with the person that
the machine is meant to become a MaluOS system before running it.

### 3. First boot (on the host)

`maluos-firstboot.service` runs once: database `maludb`, tenant `maluos`, principal
`human:admin`, the MCP listener and its token, operator database logins, then the components
in order (vault, gateway, API server, Business OS kernel). Core parts take about two minutes;
the Business OS kernel takes a couple of minutes under KVM and up to 25 under emulation. The
unit allows two hours and resumes after a reboot.

```sh
systemctl status maluos-firstboot          # ends inactive (dead), status=0/SUCCESS
journalctl -u maluos-firstboot -f          # to watch
test -e /var/lib/maluos/firstboot/done     # provisioning finished
```

Do not re-run provisioning or reboot while the unit is still active.

### 4. Check health

```sh
maluctl status
```

Expected: `provisioned: <time>`, `database: maludb (maludb_core 0.106.x)`, `tenant`,
`principal: human:admin`, `listener: http://127.0.0.1:5329 ok`, and one `component:` line per
component, each `ok`. A `FAILED` component does not block the others; read the journal for its
error and re-run with `sudo maluctl provision --force` after fixing the cause.

Further checks: `maluctl gateway status`, `maluctl secret whoami` (prints `user:<login>`),
`psql -d maludb -c 'select 1'`, `sudo systemctl --failed` (empty).

Or run `scripts/verify.sh` from this skill on the host.

### 5. Tokens

```sh
sudo maluctl token        # MCP listener token (maludb-mc2dbd, 127.0.0.1:5329)
sudo maluctl token api    # MaluDB API token (maludb-api, 127.0.0.1:8000)
```

Both services are localhost-only. **Never paste a token into a chat transcript, a ticket or a
file the person did not ask for.** Tell the person where the token is and how to print it.

### 6. A model

Local, through Ollama (works without a GPU, slowly):

```sh
sudo maluctl gateway ollama install
sudo maluctl gateway ollama pull llama3.2:3b
sudo maluctl gateway ollama status
```

NVIDIA RTX present: `sudo apt install maluos-nvidia && sudo reboot`, then `maluctl gpu status`.

Cloud, with the key kept in the vault (the gateway attaches it per request):

```sh
sudo maluctl secret add system/anthropic --provider anthropic      # prompts for the key
sudo maluctl secret grant system/anthropic --to user:$USER --level use
sudo maluctl gateway backend enable anthropic
```

Test: `maluctl gateway chat "Say hello." --model fast` then `maluctl gateway routes`.
Expected: an answer and a route row naming `ollama-local` or `anthropic`.

### 7. The Business OS

Default names `os.maluos.local` and `app.maluos.local` over HTTP, resolved on the server
itself. The person's workstation needs a hosts entry (or the real domain set before first
boot). The first super-admin is `admin@<domain>` (or the configured email); the generated
password is root-only:

```sh
sudo cat /var/lib/maluos/secrets/business-os/admin-password
```

Read it only when the person asks for it, and do not echo it anywhere else.

### 8. Updates

`maluctl update status`, `sudo maluctl update apply -y`, `maluctl update history`.

## Stop and ask the person before

- erasing a disk (the automated ISO entry wipes the first disk without asking);
- rebooting a machine others use;
- entering a provider API key (ask them to type it at the prompt; do not take it in chat);
- running the bootstrap script on a machine that was not meant to become MaluOS;
- exposing any listener beyond localhost.

## Never

- print a token, a password or a key into the conversation;
- edit `/etc/maluos/firstboot.yaml` after provisioning without telling the person that
  `sudo maluctl provision --force` is required to apply it and that it re-renders the Business
  OS names and sites;
- install Ollama or a model on a host whose disk has less than the model's size free;
- continue with an image whose signature or checksum did not verify.

## Troubleshooting map

| Symptom | Look at | Fix |
|---|---|---|
| `provisioned: no` | `journalctl -u maluos-firstboot` | fix cause (network, disk), `sudo maluctl provision --force` |
| component `FAILED` | same journal, the component's step | same |
| chat: no backend available | `sudo maluctl gateway ollama status` | pull a model or enable a cloud backend |
| Business OS page does not load | `curl -s http://127.0.0.1:8080/api/v1/health` on the host; name resolution on the workstation | hosts entry or DNS |
| forced password change after automated install | by design | choose a new password |
