fix: Start cron daemon in agent container entrypoint (#618)

The entrypoint installed a crontab but never started a cron daemon,
leaving the container idle.  Fix by running as root in the entrypoint
(cron requires it), installing the crontab for the agent user via
`crontab -u agent`, and starting cron in the foreground with `cron -f`.
Remove `USER agent` from the Dockerfile and `user: "1000:1000"` from
the compose template accordingly — cron jobs still execute as UID 1000.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-24 20:30:44 +00:00
parent e38866ab61
commit 0aa3890fb8
3 changed files with 16 additions and 16 deletions

View file

@ -192,7 +192,6 @@ services:
agents: agents:
build: ./docker/agents build: ./docker/agents
restart: unless-stopped restart: unless-stopped
user: "1000:1000"
volumes: volumes:
- agent-data:/home/agent/data - agent-data:/home/agent/data
- project-repos:/home/agent/repos - project-repos:/home/agent/repos

View file

@ -14,7 +14,8 @@ RUN useradd -m -u 1000 -s /bin/bash agent
COPY entrypoint.sh /entrypoint.sh COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh RUN chmod +x /entrypoint.sh
USER agent # Entrypoint runs as root to start the cron daemon;
# cron jobs execute as the agent user (crontab -u agent).
WORKDIR /home/agent WORKDIR /home/agent
ENTRYPOINT ["/entrypoint.sh"] ENTRYPOINT ["/entrypoint.sh"]

View file

@ -1,20 +1,22 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# entrypoint.sh — Start agent container with cron and stay alive # entrypoint.sh — Start agent container with cron in foreground
# #
# Installs crontab entries from project TOMLs found in the factory # Runs as root inside the container. Installs crontab entries for the
# mount, then runs cron in the background and tails the log. # agent user from project TOMLs, then starts cron in the foreground.
# All cron jobs execute as the agent user (UID 1000).
DISINTO_DIR="${HOME}/disinto" DISINTO_DIR="/home/agent/disinto"
LOGFILE="${HOME}/data/agent-entrypoint.log" LOGFILE="/home/agent/data/agent-entrypoint.log"
mkdir -p "${HOME}/data" mkdir -p /home/agent/data
chown agent:agent /home/agent/data
log() { log() {
printf '[%s] %s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" "$*" | tee -a "$LOGFILE" printf '[%s] %s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" "$*" | tee -a "$LOGFILE"
} }
# Build crontab from project TOMLs # Build crontab from project TOMLs and install for the agent user.
install_project_crons() { install_project_crons() {
local cron_lines="" local cron_lines=""
for toml in "${DISINTO_DIR}"/projects/*.toml; do for toml in "${DISINTO_DIR}"/projects/*.toml; do
@ -34,8 +36,8 @@ with open(sys.argv[1], 'rb') as f:
done done
if [ -n "$cron_lines" ]; then if [ -n "$cron_lines" ]; then
printf '%s\n' "$cron_lines" | crontab - printf '%s\n' "$cron_lines" | crontab -u agent -
log "Installed crontab for projects" log "Installed crontab for agent user"
else else
log "No project TOMLs found — crontab empty" log "No project TOMLs found — crontab empty"
fi fi
@ -44,8 +46,6 @@ with open(sys.argv[1], 'rb') as f:
log "Agent container starting" log "Agent container starting"
install_project_crons install_project_crons
# Keep container alive — cron runs in foreground via tail on log # Run cron in the foreground. Cron jobs execute as the agent user.
# (cron daemon needs root; since we run as agent, we use a polling approach log "Starting cron daemon"
# or the host cron can be used via docker compose exec) exec cron -f
log "Agent container ready — waiting for work"
exec tail -f /dev/null