Before you start
An application server is a MaluOS server with the Business OS component enabled, which the server image carries by default. If you have not installed a server yet, read Setting up your first server first; this guide adds what makes it the office. Three decisions are best made before the first boot, because the kernel renders its names, environment and Apache sites from them:
| Decision | Default | What to choose |
|---|---|---|
| Domain | maluos.local | a name you control, say office.example.com. The kernel serves os., app., www. and the bare name under it. |
| Scheme | http | http on a LAN; https when a TLS proxy sits in front, which is required for anything reachable from outside. |
| First super-admin | admin@<domain>, a generated password | the email and name of the person who will own the office. |
| Minimum | Recommended | |
|---|---|---|
| CPU | 2 cores | 4 cores or more |
| Memory | 4 GB | 8 GB or more; add the memory of any local model |
| Disk | 40 GB | 60 GB or more, SSD |
| Network | DNS for the four names, ports 80 and 443 from the desks |
1Set the configuration before first boot
The settings live under components.maludb-os-core in /etc/maluos/firstboot.yaml. On the cloud image, write the file with cloud-init so it is in place when the provisioning service runs:
#cloud-config
hostname: office
write_files:
- path: /etc/maluos/firstboot.yaml
content: |
tenant: {name: example}
admin_principal: {ref: human:ops, kind: human, display_name: Ops, home_scope: org}
components:
maluos-vault: {enabled: true}
maluos-gateway: {enabled: true}
maludb-api: {enabled: true}
maludb-os-core:
enabled: true
domain: office.example.com
scheme: https
admin_email: [email protected]
admin_name: Ops
# admin_password: set one, or let first boot generate it
# anthropic_api_key: sk-ant-... the office's model key, if you have one now
The keys the 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. Anything left out keeps its default.
Installed from the ISO instead? Then first boot already ran with the defaults. Edit the same file on the server and re-run provisioning. Every step is idempotent: the loopback names, the environment files and the Apache sites are rendered again for the new domain, the databases and the super-admin are kept.
sudo vi /etc/maluos/firstboot.yaml
sudo maluctl provision --force
maluctl status # component maludb-os-core ok https://os.office.example.com
2Let the kernel provision
On the first boot the component runs after the vault, the gateway and the API server. It creates the kernel's activity memory database with a MaluDB API token of its own, the kernel database with every migration in order (a resumable ledger: an interrupted run continues where it stopped), the application roles and their passwords, renders the environment files and the Apache sites for your domain, enables the eight services, health-checks the PHP API, and creates the first super-admin, the first model registry row, the cron jobs and the kernel's two agents.
This takes a couple of minutes on real hardware or KVM; the first-boot unit allows up to two hours and resumes after a reboot. Watch it, then check:
journalctl -u maluos-firstboot -f
maluctl status
curl -s http://127.0.0.1:8080/api/v1/health # the kernel's PHP API, localhost only
What you should see: component: maludb-os-core ok followed by the os. URL, and the health call answering. The super-admin's generated password is kept root-only and is also in the vault's system compartment:
sudo cat /var/lib/maluos/secrets/business-os/admin-password
3Point DNS at the server, and add TLS
Create A records (or one record and three CNAMEs) for office.example.com, www.office.example.com, os.office.example.com and app.office.example.com to the server's address. On a LAN without DNS, the desks' hosts files do the same job.
With scheme: https, a reverse proxy terminates TLS and forwards to the server's Apache on port 80 with the Host header preserved. Any proxy works: Caddy, nginx, Traefik, a Cloudflare tunnel. A minimal Caddyfile on a separate proxy host, or on the server itself with Apache moved off port 80:
office.example.com, www.office.example.com, os.office.example.com, app.office.example.com {
reverse_proxy 192.0.2.10:80
}
The kernel's own API on port 8080 stays bound to localhost; only Apache's port 80 needs to reach the proxy. What you should see: https://app.office.example.com/login loads with a valid certificate.
4Sign in as the first super-admin
Open https://os.office.example.com (or http://os.maluos.local with the defaults) and sign in with the admin email and the password from step 2. Change the password on first sign-in if it was generated. You are in the organisation with its first two agents, the Installer and the JEV prompt writer, already hired.
5Give the office a model
The office's own model endpoint is what enrolled desks and the office's agents use. First boot created one model registry row (an Anthropic model by default) that needs a key. Set anthropic_api_key, or openrouter_api_key, in the component configuration before first boot, or add the key in the kernel's settings afterwards. Keep the key on the office: a desk never holds a provider key, every desk call runs through the office's prompt ledger and budget.
A local or shared GPU can serve the office as well: the server's own gateway takes an Ollama backend, a paired MaluOS GPU server, a Mac or a Spark exactly as any MaluOS host does.
6Enrol the first desk
A desk joins with a one-time code. Mint one in Settings → Desks on the office, for the person who will own the desk, or as an operator on the server:
cd /var/www && sudo -u www-data php bin/mint_desk_code.php --help
On the desk, its owner runs sudo maluctl office enroll https://app.office.example.com --code XXXX-XXXX (the full desk side is in Installing MaluOS Desktop). What you should see: the desk listed in Settings → Desks with its owner and online; the presence sweep marks it offline when its session stops. A retired desk gets a 410 on its next heartbeat and cleans itself up.
7Publish applications and agents to desks
The desks' MaluApps window shows two catalogues: the public one, signed by the MaluOS release key, and the office's own, served from the kernel's applications table at /api/v1/desks/apps. The office's agents reach desks the same way through /api/v1/desks/agents, each with a task interface the Agents app talks to.
Where the applications stand. The kernel's default applications (Help Desk, Spaces, HR, Projects) and the integration plugin are not yet packaged as MaluOS components; the kernel installs them from git as upstream documents. MaluMail is part of the kernel in this release. The public catalogue lists MaluMail, Help Desk, CRM and Knowledgebase as office applications so that desks see them once the office serves them.
8Back it up and keep it updated
sudo maluctl secret export /root/vault-$(date +%F).bundle # the vault, encrypted to a passphrase
sudo -u postgres pg_dump maludb_os | gzip > /root/maludb_os-$(date +%F).sql.gz
sudo -u postgres pg_dump maludb | gzip > /root/maludb-$(date +%F).sql.gz
maluctl update status
sudo maluctl update apply -y
The kernel's secrets are under /var/lib/maluos/secrets/business-os; back that directory up with the dumps. Security and MaluOS updates install themselves; a kernel update arrives as a new maludb-os-core package and runs its new migrations through the same ledger.
If something is wrong
maluctl statusshowsmaludb-os-core FAILED. The journal names the step. The common causes are the network being unavailable during the first boot and the PHP API not becoming healthy in time. Fix, thensudo maluctl provision --force; migrations already applied are skipped.- The login page loads but a redirect goes to the wrong name. The scheme or domain in the configuration does not match the proxy. Fix the file and re-provision.
- A desk's enrolment fails with a 4xx. The code is one-time and expires; the URL must be the
app.name; the desk must reach port 443 (or 80 on a LAN). - Agents in the office have no model. Step 5: the model registry row needs a key.
- Still stuck. Open an issue at github.com/maludb/maluos/issues with
maluctl statusand the relevant journal lines.
What next
- Make this office the estate's coordinator. Servers join with
sudo maluctl estate join URL --code CODEand report their health, agents and GPUs; the operators' Estate page on the desks shows them. - Hire agents. The kernel's Agent HR and the desks' Agents app meet here: an agent the office hires can be given tasks from any enrolled desk.
- Add the MaluDB API for your own tools: it is already running on the server;
sudo maluctl token apiis the tenant token.