Installation alternatives

Install without curl-pipe-bash

The default curl … | bash install is convenient, but if you'd rather inspect what runs first, verify the binary, or roll the agent out via configuration management, this page is for you.

Inspect the install script before running it

The install script is plain Bash. Read it before piping to your shell:

curl -fsSL https://astrari.io/install.sh -o install.sh
less install.sh                   # read it
bash install.sh https://api.astrari.io YOUR_AGENT_TOKEN

Or audit it on every host by hosting it yourself: drop a copy into your config-management repo and run that copy in your provisioning pipeline.

Verify the agent binary by checksum

Every release publishes a SHA-256 sum alongside each binary. Download the binary, download the sum, and verify before executing:

amd64

curl -fsSL -o astrari-agent \
  https://astrari.io/agent/download/linux-amd64
curl -fsSL -o astrari-agent.sha256 \
  https://astrari.io/downloads/astrari-agent-linux-amd64.sha256

# Compare — should print "astrari-agent: OK"
sed 's| .*| astrari-agent|' astrari-agent.sha256 | sha256sum -c -

arm64

curl -fsSL -o astrari-agent \
  https://astrari.io/agent/download/linux-arm64
curl -fsSL -o astrari-agent.sha256 \
  https://astrari.io/downloads/astrari-agent-linux-arm64.sha256

sed 's| .*| astrari-agent|' astrari-agent.sha256 | sha256sum -c -

Beyond download verification, the agent itself verifies every self-update against an Ed25519 signature served from our API. A binary that doesn't verify will be rejected — even if our CDN is compromised. See the Security page for the full chain.

Manual install (no script)

If you'd rather skip the install script entirely, you can place the binary, write a config, install the systemd units, and start the timer by hand. The agent itself does no installation work — it just runs scans.

1. Place the binary

# After verifying the checksum (see above)
sudo install -m 0755 astrari-agent /usr/local/bin/astrari-agent

2. Write the config

sudo install -d -m 0700 /etc/astrari
sudo tee /etc/astrari/agent.conf > /dev/null <<'EOF'
api_url=https://api.astrari.io
token=YOUR_AGENT_TOKEN
EOF
sudo chmod 0600 /etc/astrari/agent.conf

3. Install the systemd service

sudo tee /etc/systemd/system/astrari-agent.service > /dev/null <<'EOF'
# Managed by astrari-agent. The agent rewrites this file to match the unit
# built into its binary; override with a drop-in instead:
#   /etc/systemd/system/astrari-agent.service.d/override.conf

[Unit]
Description=Astrari Security Agent
After=network-online.target
Wants=network-online.target
# Tolerate transient failures without disabling the timer (20 failures in
# 5 minutes cannot happen at a 30s cadence; the agent self-recovers).
StartLimitIntervalSec=300
StartLimitBurst=20

[Service]
Type=oneshot
TimeoutStartSec=6h
ExecStart=/usr/local/bin/astrari-agent --config /etc/astrari/agent.conf
User=root
StandardOutput=journal
StandardError=journal
SyslogIdentifier=astrari-agent
NoNewPrivileges=yes
# Memory for the agent's own cgroup (scanners, synchronous restic): above
# 75% of RAM the kernel throttles and reclaims (MemoryHigh); 90% is the hard
# cap (MemoryMax), so a runaway child cannot exhaust the host while a
# ClamAV signature load (~1.2-1.6 GB) still fits on small hosts. Transient
# units started via systemd-run (package managers, vault jobs) are
# separate cgroups.
MemoryAccounting=yes
MemoryHigh=75%
MemoryMax=90%
EOF

4. Install the systemd timer (every 30 sec, 20 sec jitter)

sudo tee /etc/systemd/system/astrari-agent.timer > /dev/null <<'EOF'
[Unit]
Description=Astrari Security Agent heartbeat

[Timer]
OnBootSec=30sec
OnActiveSec=30sec
OnUnitActiveSec=30sec
RandomizedDelaySec=20sec
Persistent=true
Unit=astrari-agent.service

[Install]
WantedBy=timers.target
EOF

5. Enable and start

sudo systemctl daemon-reload
sudo systemctl enable --now astrari-agent.timer
sudo systemctl status astrari-agent.timer
journalctl -u astrari-agent -f

Optional: install the OS scanners (Trivy, ClamAV, rkhunter, MalDet) via your usual package manager. The agent will report findings from whatever scanners it can find. Without them you'll still get configuration, SSH, firewall, file-integrity, and OS-update findings.

Ansible role — minimal example

Drop this into roles/astrari_agent/tasks/main.yml and pass astrari_token as a per-host variable from your inventory or vault.

roles/astrari_agent/tasks/main.yml

---
- name: Resolve architecture
  ansible.builtin.set_fact:
    astrari_arch: "{{ 'arm64' if ansible_architecture in ['aarch64','arm64'] else 'amd64' }}"

- name: Download Astrari agent binary
  ansible.builtin.get_url:
    url: "https://astrari.io/agent/download/linux-{{ astrari_arch }}"
    dest: /usr/local/bin/astrari-agent
    mode: "0755"
    # Pin the exact sha256 you've reviewed — bump on each release.
    # Get it from https://astrari.io/downloads/astrari-agent-linux-{{ astrari_arch }}.sha256
    checksum: "sha256:{{ astrari_agent_sha256 }}"

- name: Create agent config dir
  ansible.builtin.file:
    path: /etc/astrari
    state: directory
    mode: "0700"

- name: Write agent config
  ansible.builtin.copy:
    dest: /etc/astrari/agent.conf
    mode: "0600"
    content: |
      api_url=https://api.astrari.io
      token={{ astrari_token }}

- name: Install systemd unit
  ansible.builtin.copy:
    dest: /etc/systemd/system/astrari-agent.service
    content: |
      # Managed by astrari-agent. The agent rewrites this file to match the unit
      # built into its binary; override with a drop-in instead:
      #   /etc/systemd/system/astrari-agent.service.d/override.conf

      [Unit]
      Description=Astrari Security Agent
      After=network-online.target
      Wants=network-online.target
      # Tolerate transient failures without disabling the timer (20 failures in
      # 5 minutes cannot happen at a 30s cadence; the agent self-recovers).
      StartLimitIntervalSec=300
      StartLimitBurst=20

      [Service]
      Type=oneshot
      TimeoutStartSec=6h
      ExecStart=/usr/local/bin/astrari-agent --config /etc/astrari/agent.conf
      User=root
      StandardOutput=journal
      StandardError=journal
      SyslogIdentifier=astrari-agent
      NoNewPrivileges=yes
      # Memory for the agent's own cgroup (scanners, synchronous restic): above
      # 75% of RAM the kernel throttles and reclaims (MemoryHigh); 90% is the hard
      # cap (MemoryMax), so a runaway child cannot exhaust the host while a
      # ClamAV signature load (~1.2-1.6 GB) still fits on small hosts. Transient
      # units started via systemd-run (package managers, vault jobs) are
      # separate cgroups.
      MemoryAccounting=yes
      MemoryHigh=75%
      MemoryMax=90%
  notify: reload systemd

- name: Install systemd timer
  ansible.builtin.copy:
    dest: /etc/systemd/system/astrari-agent.timer
    content: |
      [Unit]
      Description=Astrari Security Agent heartbeat

      [Timer]
      OnBootSec=30sec
      OnActiveSec=30sec
      OnUnitActiveSec=30sec
      RandomizedDelaySec=20sec
      Persistent=true
      Unit=astrari-agent.service

      [Install]
      WantedBy=timers.target
  notify: reload systemd

- name: Enable and start the timer
  ansible.builtin.systemd:
    name: astrari-agent.timer
    enabled: true
    state: started
    daemon_reload: true

Per-host inventory needs only astrari_token and astrari_agent_sha256 (per-arch). Pin the sha256 in source control so a tampered binary fails the run with a loud checksum mismatch.

Outbound network requirements

The agent only ever speaks outbound, over HTTPS:

  • ▸ api.astrari.io:443 — heartbeat, results upload, config fetch
  • ▸ astrari.io:443 — agent self-updates

No inbound ports need to be opened on the server. If your egress is restricted, allow-list the two hostnames above.