Operación
Qué comandos usar para validar, arrancar, recargar, leer logs y diagnosticar, más los errores que aparecen una y otra vez. Todo lo de esta página funciona sobre un despliegue real de dos procesos (ver Instalación).
Validar
exyonq validate -c /etc/exyonq/https.toml
Salida correcta:
warning: `exyonq validate` is transitional; prefer `exyonqctl config lint` (or `exyonqctl config test` for RuntimePlan compile)
config OK: /etc/exyonq/https.toml
OK
Con un error, el código de salida es 1 y el diagnóstico señala línea y campo:
error[EXY-CONFIG-0002]: TOML parse error at line 6, column 23
|
6 | match = { path = "/", upstream = "nope" }
| ^^^^^^^^
unknown field `upstream`, expected one of `path`, `host`, `suffix`, `regex`, `header`, `header_value`
validate comprueba parseo y validación del IR. Para compilar el plan de ejecución completo sin publicar nada, usa exyonqctl config test; para una preflight de recarga, exyonqctl config reload --check.
Arrancar
exyonq serve -c /etc/exyonq/https.toml
En systemd, serve corre bajo el usuario del servicio. La unidad publicada ejecuta la validación como ExecStartPre, así que un TOML roto no llega a arrancar. El proceso no implementa sd_notify; no uses Type=notify.
Variables de entorno relevantes:
| Variable | Efecto |
|---|---|
EXYONQ_CONFIG | Ruta usada por la unidad del paquete |
EXYONQ_CONTROL_SOCKET | Socket Unix del plano de control |
EXYONQ_ACME_CHALLENGE_DIR | Directorio compartido de retos HTTP-01 |
EXYONQ_WAF_MODE, EXYONQ_WAF_ENFORCE, EXYONQ_WAF_ABUSE, EXYONQ_WAF_ABUSE_RPS, EXYONQ_WAF_ABUSE_BURST | Anulan el WAF del IR |
EXYONQ_CACHE_PURGE_SOCKET, EXYONQ_CACHE_PURGE_TOKEN | Purga autenticada de caché |
Recargar
exyonqctl config reload /etc/exyonq/https.toml --check # preflight, no publica
exyonqctl config reload /etc/exyonq/https.toml --diff # clasifica el cambio
exyonqctl reload -s /run/exyonq/https.sock -c /etc/exyonq/https.toml
exyonqctl status -s /run/exyonq/https.sock
--diff compara huellas y enumera rutas:
current_generation = 1
current_fingerprint = 0960bed9…:m0c1r0
candidate_fingerprint = 1f3c2a77…:m0c1r0
routes_added = ["exyonq_dev_assets"]
routes_removed = []
routes_unchanged = 26
IDENTICAL_RELOAD = NO_OP
DYNAMIC_INPLACE = FORBIDDEN
H3_RELOAD_SUPPORT = SAME_LISTENER_CONFIG_ONLY
La línea base sale de --baseline <IR anterior> o de un fichero lateral {config}.exyonq-applied-routes.v1.json que se escribe tras publicar. Si lo borras, la siguiente recarga vuelve a decir «todo añadido» una vez.
Una recarga fallida no toca la generación viva: se descarta el candidato. Si el cambio altera una dirección de escucha o habilita un protocolo nuevo, la recarga no sirve: reinicia (EXY-RELOAD-0005).
Logs y niveles
[logging].level acepta la sintaxis de EnvFilter: info, debug, o por objetivo (exyonq=debug,exyonq_waf=info,warn). format elige text o json; con json cada línea es un objeto con los mismos campos.
Línea de acceso real:
INFO exyonq_core::observability: access event="access" method=GET path=/health status=200 protocol=http duration_ms=0 client_ip="64.181.243.113" request_id=8b9760756feff3b2206a59b5dfea539b outcome="probe"
outcome resume el resultado: ok, probe para /health, forbidden para un 403. Cada petición lleva un request_id, que es la clave para cruzar la línea de acceso con una violación de WAF.
journalctl -u exyonq-sites -f
journalctl -u exyonq-sites --since "-15 min" | grep 'status=5'
journalctl -u exyonq-sites | grep 'waf violation'
Destinos alternativos: [logging.file] (con rotación propia por tamaño y keep), [logging.syslog] y [logging.journald]. Si prefieres logrotate, el fichero publicado recarga el proceso por el socket de control en lugar de mandar SIGHUP.
ACME
HTTP-01 es el modo por defecto y no necesita credenciales DNS: el proceso registra el token y el mundo lo pide por el puerto 80.
[server.tls.acme]
enabled = true
email = "security@exyonq.dev"
domains = ["exyonq.dev", "www.exyonq.dev"]
challenge = "http-01"
Con dos procesos, el del :443 emite y el del :80 responde el reto. Ambos deben apuntar EXYONQ_ACME_CHALLENGE_DIR al mismo directorio estable, fuera de RuntimeDirectory (por ejemplo /var/lib/exyonq-http01, 0750, propietario del servicio). El proceso escribe <dir>/<token> de forma atómica con modo 0600 y lo borra al terminar; solo se sirven nombres de token seguros.
Renovación: se usa ARI (ACME Renewal Information) cuando la CA lo soporta y, si no, se renueva 30 días antes del vencimiento. Los certificados se escriben de forma atómica y el aceptador TLS se reconstruye con la configuración vigente, sin reiniciar.
INFO exyonq_acme::manager: ACME HTTP-01 challenge registered token=N-jBkXUc3ssTrcN83YiKUxcI-n-Plav3JkhO3UmAcnM domain=www.exyonq.dev
INFO exyonq_acme::manager: ACME certificate issued cert=/etc/exyonq/tls/exyonq.pem key=/etc/exyonq/tls/exyonq.key
INFO exyonq_acme::manager: ACME ARI renewal window renewal_window_start=1798673700
Si el pedido falla, el fallo queda en el journal y el certificado vigente sigue sirviéndose; si no hay material válido en disco, el listener TLS falla cerrado: prefiere no negociar antes que servir un certificado caducado o de otro host. Un certificado emitido a mano en cert/key sigue funcionando aunque ACME esté deshabilitado.
La cuenta se guarda en account_path o en <cert>.acme-account.json, con modo 0600, y se reutiliza entre ejecuciones.
Diagnóstico
| Síntoma | Qué mirar |
|---|---|
| El servicio no arranca | journalctl -u <unidad> -n 50; exyonq validate -c … |
| 502 en un proxy | Que el backend escuche en el target; timeout_ms; journal del backend |
| 403 inesperado en una ruta | grep 'waf violation'; rule_id y field dicen qué regla y qué parte de la petición |
| 404 de un estático | root de la ruta, el prefijo del match y si el fichero existe |
| El certificado no se renueva | EXYONQ_ACME_CHALLENGE_DIR en ambas unidades; que el puerto 80 sea alcanzable; logs exyonq_acme |
| Cambios que no se aplican | exyonqctl status (huella y generación), después config reload --diff |
Comandos de apoyo:
exyonqctl status -s /run/exyonq/https.sock --format json
exyonqctl cache stats --url http://127.0.0.1:9090/metrics
exyonqctl cache explain --config /etc/exyonq/https.toml https://exyonq.dev/
exyonqctl purge url -s /run/exyonq/purge.sock --token "$TOKEN" exyonq https exyonq.dev /
exyonqctl drain -s /run/exyonq/https.sock
exyonqctl shutdown -s /run/exyonq/https.sock
status devuelve una línea como status ok process_state=serving generation=1 fingerprint=…:m0c1r0 uptime_s=418 active_connections=6 draining=false reload_in_progress=false version=0.9.0.
Errores típicos
| Mensaje | Causa |
|---|---|
config must define at least one [[server]] block | Falta el listener |
unknown field … | Campo que no existe en el IR, o mal escrito |
route match must set exactly one of path, suffix, or regex | Dos patrones a la vez en match |
duplicate route match identity host=… pattern=… | Dos rutas con el mismo host, tipo y patrón |
server references unknown route: X | routes lista un nombre que no existe |
route X references unknown upstream: Y | upstream mal escrito o no declarado |
route X must define upstream, root, redirect, rewrite, or fastcgi | Ruta sin acción |
route X defines conflicting actions | Dos acciones en la misma ruta |
http3_listen is only honored on the first [[server]] block | http3_listen en un bloque secundario |
http3_listen cannot be combined with tls.strict_sni/… | Opciones que QUIC no honra |
modules.metrics.scrape_bearer_token is required when metrics is enabled and any server.listen or server.http3_listen is non-loopback | Métricas expuestas sin token |
health_check.timeout_ms must be <= interval_ms | Sondeo mal dimensionado |
upstream X: max_connect_retries=2 exceeds Cap021 v1 max 1 | Reintentos de conexión por encima del máximo |
Checklist antes de reiniciar
exyonq validate -c <config>devuelveOKpara cada fichero del despliegue (http.tomlyhttps.toml).- Si el cambio es de rutas, upstreams o material TLS, prueba primero
exyonqctl config reload <config> --check; si pasa, recarga en lugar de reiniciar. - Si el cambio toca
listen,http3_listeno habilita un protocolo, el reinicio es obligatorio: planifica la ventana. - Copia el TOML vigente y anota la huella de
exyonqctl status; es tu vuelta atrás. - Comprueba que el usuario del servicio puede leer
cert/keyy escribir en el directorio de certificados y en el de retos. - Tras reiniciar:
statusconprocess_state=serving,curl -sS -o /dev/null -w '%{http_code}' https://<host>/healthyjournalctlsin errores nuevos.