# Troubleshooting

> Diagnose installation, sign-in, enrolment, collection and terminal problems.

## Where to look

| Source | Command or place |
|---|---|
| Container state | `docker compose ps` |
| Service logs | `docker compose logs --tail 100 api` (or `worker`, `gateway`, `web`, `openbao`) |
| Failed jobs and their error | **Actividad** (Activity), filter **Fallidas** (Failed) |
| Who did what | **Ajustes → Auditoría** (Settings → Audit) |
| API health | `curl -s http://127.0.0.1:8080/api/health` |

## Installation

### `init.sh`: Falta .env

Run `cp .env.example .env` and edit it before `init.sh`.

### `docker compose up` fails with `… es obligatoria`

Compose refuses to start when a required variable is empty:

- `POSTGRES_PASSWORD`, `ADMIN_EMAIL`, `ADMIN_PASSWORD` or `INFRAWARD_REGISTRY`: set them in `.env`.
- `OPENBAO_ROLE_ID`, `VAULT_OPENBAO_ROLE_ID` or `TERMINAL_OPENBAO_ROLE_ID` (*la escribe init.sh*): run `./init.sh`. This also happens after an upgrade that added a new AppRole.

### `init.sh`: OpenBao ya está inicializado pero falta secrets/openbao/init.json

OpenBao is configured and the file with its root token was removed, as recommended. If nothing needs to change, you do not have to run `init.sh`. If an upgrade requires it, copy `init.json` back from where you stored it, run `init.sh` and remove the file again.

### Podman: `unknown mount option "uid=70"`

Some Podman versions do not accept the `uid=` and `gid=` options that `compose.yml` uses for its `tmpfs` mounts. Replace them with Podman's `U` option, which assigns the tmpfs to the container user:

```bash
sed -i -E 's/:uid=[0-9]+,gid=[0-9]+,mode=/:U,mode=/' compose.yml
```

Keep the change in mind when you update `compose.yml` on upgrades.

### The API restarts in a loop

Check `docker compose logs api`. The API validates its configuration on start and stops with `Configuración de entorno inválida: …` naming the variable. Common causes:

- `PUBLIC_URL tiene que ser https en producción`: set `INFRAWARD_PUBLIC_URL` to an `https://` URL.
- A variable added in an override file with a value out of range. See [Configuration variables](/docs/configuration.md) for the allowed values.

### OpenBao shows an error in settings

**Ajustes → Secretos** shows `openbao` with a problem when the API or worker cannot log in with their AppRole:

1. Check `docker compose ps openbao` is `healthy`. If it is sealed after a restart and you removed the static seal, unseal it.
2. Check the role IDs in `.env` and the files in `secrets/openbao/`. If in doubt, run `./init.sh --rotate-secret-id` and `docker compose restart api worker gateway`.

## Sign-in and sessions

### Signing in returns to the login page

The browser dropped the session cookie, which is `Secure`. This happens when InfraWard is opened over plain HTTP on any host other than `localhost`. Serve it over HTTPS. See [HTTPS and reverse proxy](/docs/reverse-proxy.md).

### Safari does not keep the session on `localhost`

Safari rejects `Secure` cookies on `http://localhost`. Use Chrome or Firefox for local tests, or put InfraWard behind HTTPS.

### Write actions fail with 403

The API rejects write requests whose `Origin` does not match `INFRAWARD_PUBLIC_URL`. Check that the URL in `.env` is exactly the one in the browser's address bar (scheme, host and port), then run `docker compose up -d`.

### OIDC sign-in says the email is not verified

The message *… no marca tu email como verificado* means the provider did not send `email_verified: true`, so InfraWard does not create the account. Verify the email in the provider. If your provider never sends that claim (Microsoft Entra ID), set `OIDC_ALLOW_UNVERIFIED_SIGNUP: "true"` in `api`. See [How accounts are created and updated](/docs/authentication.md#how-accounts-are-created-and-updated).

### The audit log shows the proxy's IP for every user

`INFRAWARD_TRUSTED_PROXY` is unset or does not match the reverse proxy. See [Find the proxy's address](/docs/reverse-proxy.md#find-the-proxys-address).

### The first administrator was not created

`ADMIN_EMAIL` and `ADMIN_PASSWORD` are only read when the database has no users, and the password needs at least 12 characters. Check `docker compose logs api` for a warning. If a user already exists, changing these variables has no effect.

### An administrator lost the authenticator and the recovery codes

Another administrator resets the second factor from **Ajustes → Usuarios**. There is no reset from the command line, which is why you should always keep at least two administrator accounts (or an emergency local account when you use OIDC).

## Enrolment

### `curl: (6) Could not resolve host` or `(7) Failed to connect`

The server cannot reach `INFRAWARD_PUBLIC_URL`. Check DNS and firewalls from the server, or use the **Script embebido (VPN/NAT)** tab, which needs no connection to InfraWard.

### The page stays on **Esperando clave** (Waiting for key)

The script did not finish or could not report back. Read the script output on the server. Each line starts with `[infraward]`; the last one before an error explains it. The token is single-use: generate a new command to try again.

### The script stops because the clock is behind

In certificate mode, certificates are valid from 30 seconds before their creation. A server clock more than 5 minutes behind would reject them. Enable NTP (`timedatectl set-ntp true`) and run the command again.

### `sshd -t` fails and the script rolls back

The script validates sshd before reloading it. The usual cause is an `sshd_config` without `Include /etc/ssh/sshd_config.d/*.conf` or an earlier file that already sets `TrustedUserCAKeys` or `AuthorizedPrincipalsFile` for all users. Fix the configuration and run the command again, or enrol with a static key.

### The script stops on `~/.ssh` or `authorized_keys`

In static key mode the script writes `authorized_keys` as the `inventory` user and checks the paths first. It stops without changing anything when:

- `~/.ssh` or `authorized_keys` is a symbolic link. The script does not follow it, because a link such as `~/.ssh -> /root/.ssh` would make root write somewhere else. InfraWard never creates such links: check the server, someone may have signed in as `inventory`.
- `~/.ssh` or `authorized_keys` belongs to another user, or `authorized_keys` is not a regular file or has more than one hard link.
- The account already existed and its home directory does not belong to it (`'inventory' no puede crear …/.ssh`). Give the user ownership of its home directory, or remove the account and let the script create it.

Fix the cause and run the command again, or generate a new one if the token has expired.

### **Conectando** (Connecting) fails with a host key error

The host key presented to the worker does not match the fingerprints the script reported. Check that the address you entered reaches the right machine (not a load balancer or a different host behind NAT).

## Collection and scans

### Find out why a collection or scan failed

Open the asset's **Recolecciones** tab, or the job in **Actividad**, and select the failed run. Its log shows the step and command that failed, with the end of its error output. See [Run logs](/docs/inventory.md#run-logs).

### Collection fails with `Failed to create temporary directory`

Fixed in InfraWard 0.5.2: upgrade. On 0.5.0 and 0.5.1, Ansible cannot write its temporary directory in the worker, whose root filesystem is read-only. Add a writable tmpfs for it in an override file (and add the file to `COMPOSE_FILE`):

```yaml title="compose.override.yml"
services:
  worker:
    tmpfs:
      - /tmp:uid=1000,gid=1000,mode=0700
      - /home/node/.ansible:uid=1000,gid=1000,mode=0700
```

Then run `docker compose up -d worker` and select **Recolectar de nuevo** on the affected servers.

### Collection is partial

The asset page shows *La última recolección fue parcial* with the reason. For example, without `ss` or `netstat` on the server, ports are read from `/proc/net` without the owning process. Install `iproute2` to get process names.

### Packages are missing on a Proxmox CT

Install `python3` in the CT. Collection inside CTs runs through `pct exec` from the node and needs Python.

### Scans wait for a long time after installation

On the first start, the worker downloads about 3 GB of Trivy data. Scans wait for it. Follow it with `docker compose logs -f worker | grep -i trivy`.

### Podman containers do not appear

- The server shows **sudoers desactualizado**: Podman needs sudoers version 8. Regenerate the enrolment command and run it again.
- Rootless containers: the user must be enabled in the **Usuarios de Podman** card, and collected again afterwards. A user in **Sin socket** (No socket) is enabled but has no active `podman.socket`: run **Habilitar** again, which also turns on linger so the socket survives the end of the user's sessions.
- A user with no running containers, no socket and storage outside `~/.local/share/containers` is not detected. Add them with `--podman-users`.

### A server shows hundreds of kernel vulnerabilities

Upgrade to InfraWard 0.6.0 or later. Earlier versions counted the findings of installed kernels that are not running, which dnf keeps on RHEL-family servers.

### An image is marked as not scanned

The reason is shown on the image: larger than `TRIVY_MAX_IMAGE_SIZE` (4 GiB) or longer than `TRIVY_SCAN_TIMEOUT` (15 minutes). Raise the limits on the worker or exclude the image from scanning.

## Alerts

### **Enviar prueba** fails on a webhook

The last error is shown on the channel. Usual causes:

- The URL resolves to a private address, or uses `http:`: the worker rejects it unless `ALERTS_WEBHOOK_ALLOW_PRIVATE` or `ALERTS_WEBHOOK_ALLOW_HTTP` is `true` on the worker.
- The receiver redirects: redirects are not followed. Use the final URL.
- The receiver takes longer than `ALERTS_TIMEOUT_MS` (10 seconds) to answer.

### Email channels do not send

The worker has no SMTP server configured (`SMTP_HOST`), or the server does not offer TLS. See [Configuration variables](/docs/configuration.md#alerts).

## Web terminal

### **Abrir terminal** is not available

The server needs certificate mode and administration access enabled, OpenBao must be running and you need permission on the asset. See [Web terminal](/docs/terminal.md#requirements).

### The terminal says *No se pudo cargar la terminal*

Fixed in InfraWard 0.6.0: production builds of earlier versions could not load the terminal component. Upgrade.

### Confirming your identity with authentik fails the second time

Fixed in InfraWard 0.6.0. authentik ignores `max_age=0`, which earlier versions sent, and returned a stale sign-in time from the second confirmation in the same browser session. Upgrade.

### The terminal closes right after opening

The reverse proxy is not forwarding WebSocket upgrades on `/gateway/`, or it closes idle connections too early. See [HTTPS and reverse proxy](/docs/reverse-proxy.md#what-the-proxy-must-do).

---

HTML page: https://infraward.net/docs/troubleshooting/
