execd is a small self-hosted daemon for triggering explicitly configured commands over HTTP and inspecting their job history. It is intended for private homelabs and trusted callers, not direct public-internet exposure.
go install github.com/ladzaretti/execd@latestcurl -sSL https://raw.githubusercontent.com/ladzaretti/execd/main/install.sh | bashThe installer downloads the latest Linux release and installs execd to /usr/local/bin.
git clone https://github.com/ladzaretti/execd.git
cd execd
make buildThe binary is written to bin/execd.
By default, execd reads ~/.config/.execd.toml. The configuration file must have 0600 permissions.
Generate a starting point, set a real password, and add or replace endpoints:
mkdir -p ~/.config
execd config generate > ~/.config/.execd.toml
chmod 600 ~/.config/.execd.toml[server]
# Log level (default: info).
# log_level = ''
# SQLite database path (default: $XDG_CACHE_HOME/.execd.d/execd.sqlite; ~/.cache/.execd.d/execd.sqlite when unset).
# database_path = ''
# Bearer token for protected endpoints (required).
password = ''
# Listen address (required).
listen_addr = ':8443'
# Enable TLS.
# tls = false
# TLS certificate file (required when tls is true).
# cert_file = ''
# TLS private key file (required when tls is true).
# key_file = ''
[[endpoints]]
# Human-readable route description.
summary = 'Health check.'
# Route path below /exec (required).
path = '/ping'
# HTTP method (default: POST).
method = 'GET'
# Fixed command and arguments (required).
cmd = ['/usr/bin/echo', 'pong']
# Environment variables inherited from execd.
# env_allowlist = []
# Start without waiting for completion.
# detached = false
# UID to run as (requires execd to run as root).
# uid = 0
# GID to run as (requires execd to run as root).
# gid = 0
# Maximum execution time as a Go duration (for example: 30s, 5m); not available for detached commands.
# timeout = ''
# Disable authentication for this execution route.
# no_auth = falseConfigured paths are mounted below /exec. A missing method defaults to POST. no_auth makes only that execution endpoint public; job and route APIs always require the server password.
env_allowlist copies selected variables from execd's environment. Path and query parameters are also passed to the command as upper-snake-case environment variables: POST /exec/backup/home?dryRun=true supplies NAME=home and DRY_RUN=true.
$ execd -h
Usage:
./bin/execd [flags]
./bin/execd config generate
./bin/execd version
The execd daemon exposes configured commands as authenticated HTTP endpoints.
-config string
config file path (default: $XDG_CONFIG_HOME/.execd.toml; ~/.config/.execd.toml when unset)Start it with a config file:
execd -config /etc/execd.tomlFor a systemd deployment, set a persistent database location in /etc/execd.toml. systemd does not provide a user cache directory by default:
[server]
database_path = "/var/lib/execd/execd.sqlite"Create the directory, then copy the release's example unit:
sudo install -d -m 0700 /var/lib/execd
sudo curl -fsSL \
-o /etc/systemd/system/execd.service \
"https://raw.githubusercontent.com/ladzaretti/execd/$(execd version)/systemd/execd.service"
sudo chmod 0644 /etc/systemd/system/execd.service
sudo systemctl daemon-reload
sudo systemctl enable --now execdTrigger a protected endpoint and inspect its asynchronous job:
curl -H 'Authorization: Bearer change-me' \
http://localhost:8443/exec/ping
curl -H 'Authorization: Bearer change-me' \
http://localhost:8443/jobs/<job-id>The execution request returns 202 Accepted with a job ID and relative job location. Jobs can be listed with GET /jobs, inspected with GET /jobs/{id}, canceled with DELETE /jobs/{id}, and configured routes discovered with GET /user-routes.
Keep execd on a private network and use a strong server password. Commands and their argv are fixed in configuration, but request path/query parameters become command environment variables; treat callers as trusted and validate values in the invoked command when appropriate.