The gitea repo had stale copies of both files. The old entrypoint.sh
called __initialize_default_templates, __initialize_config_dir, and
__initialize_data_dir which don't exist in the old functions library,
causing container startup failures.
Replace both with the current template versions (202606041210-git).
The new entrypoint.sh no longer calls those missing functions.
Set CONTAINER_NAME=gitea and description to match the service.
- rootfs/usr/local/bin/entrypoint.sh: update to 202606041210-git template
- rootfs/usr/local/etc/docker/functions/entrypoint.sh: update to current template
.claude/settings.local.json
rootfs/usr/local/bin/entrypoint.sh
rootfs/usr/local/etc/docker/functions/entrypoint.sh
act_runner runs as the git user which has no access to /root (Docker's
WORKDIR). Setting WORK_DIR=/data/act_runner ensures the service starts
from a directory the git user can access, preventing any git or file
operations from inadvertently referencing /root.
05-dockerd.sh runs as root so no fix needed there.
- rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh: set WORK_DIR=/data/act_runner
rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh
Docker's WORKDIR is /root and the git user has no read permission on
that directory. When gitea starts via gosu git, git inherits /root as
the working directory, calls getcwd(), constructs /root/.git, and
fails with "fatal: error reading '/root/.git'" — crashing gitea.
Setting WORK_DIR=/data/gitea causes the init functions library to cd
to /data/gitea before launching the service, so git never sees /root.
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: set WORK_DIR=/data/gitea
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
chmod 0600 $DATA_DIR/ssh/* fails with "cannot access" when the
directory is empty (first boot before keys are generated, or when
keys are symlinks in /config/ssh). Replace the glob with find -type f
which silently handles empty directories.
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: use find -exec chmod instead of glob for ssh key permissions
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
:host labels run jobs directly on the container filesystem with no
isolation. Replace all arch-specific :host labels with
:docker://ubuntu:latest so every job runs inside its own container
regardless of the runner host architecture.
- rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh: amd64/arm64/linux labels use docker://ubuntu:latest not :host
- README.md: remove :host from external runner label examples
README.md
rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh
Detect the host architecture at container startup and prepend
arch-specific labels to RUNNER_LABELS so matrix workflows can target
native runners by architecture:
runs-on: amd64 → dispatched to x86_64 runners
runs-on: arm64 → dispatched to aarch64 runners
runs-on: linux/amd64 / runs-on: linux/arm64 (OCI-style)
On x86_64: adds amd64:host and linux/amd64:host
On aarch64: adds arm64:host and linux/arm64:host
This enables a dedicated ARM64 server running the same image to register
native arm64 runners against the same Gitea instance, allowing full
multi-arch matrix CI without emulation.
- rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh: detect arch, prepend arch labels to RUNNER_LABELS
rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh
Docker writes /etc/resolv.conf at container start before PID1 launches,
and again asynchronously when the network finishes initializing. The
entrypoint's early copy gets overwritten by Docker's second write.
Re-applying the custom resolv.conf in __run_precopy (init.d phase)
happens after Docker's network setup is complete, so the search . and
options ndots:0 settings stick for the full container lifetime.
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: copy custom resolv.conf in __run_precopy
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
DATABASE_DIR fix:
The previous override hardcoded $DATA_DIR/db/sqlite, ignoring
DATABASE_DIR_SQLITE when it was explicitly set via env var. Changed
to respect DATABASE_DIR_SQLITE and only fall back to $DATA_DIR/db/sqlite
when the env var is not provided.
README:
- docker run and compose examples updated to match actual working flags:
--cgroupns private, --tty, --cap-add CHOWN/SYS_TIME/SYS_ADMIN,
--hostname FQDN, --domainname, GITEA_PROTO, DATABASE_DIR_SQLITE
with a separate sqlite volume mount
- Removed non-functional vars from examples (CONTAINER_PROTOCOL,
CONTAINER_DEFAULT_DATABASE_TYPE, DATABASE_BASE_DIR, WEB_PORT)
- Added DATABASE_DIR_SQLITE to the database env var table
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: respect DATABASE_DIR_SQLITE env var
- README.md: fix docker run/compose examples, add DATABASE_DIR_SQLITE to table
README.md
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
DATABASE_DIR fix (08-gitea.sh):
DATABASE_SERVICE_TYPE="sqlite" triggers a generic block that appends
/$SERVER_NAME to /data/db/sqlite. Before the SERVER_NAME fix that was
empty giving /data/db/sqlite//gitea.db; even after it would be
/data/db/sqlite/<hostname> not under DATA_DIR. Re-pin DATABASE_DIR to
$DATA_DIR/db/sqlite after the generic block.
README rewrite:
- Full env var reference table (GITEA_SERVER, GITEA_PROTO, GITEA_NAME,
GITEA_ADMIN, GITEA_EMAIL_*, GITEA_SQL_*, ACT_RUNNER_FALLBACK_VERSION,
RUNNERS_START, DOMAIN, DEBUGGER)
- Volume and port tables
- Production notes: --privileged required, GITEA_SERVER must be set,
mailer disabled by default, DNS override explained
- Canonical section order: Docker → Development → License
- README.md: full rewrite with env vars, volumes, ports, and production notes
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: fix DATABASE_DIR to always use $DATA_DIR/db/sqlite
README.md
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
Two substitution bugs fixed:
act_runner (zz-act_runner.sh):
- REPLACE_RUNNER_* tokens were only substituted inside the registration
block (guarded by SYS_AUTH_TOKEN + runners file absence). If gitea
wasn't ready on first boot, the file was copied with tokens intact
and never substituted on subsequent boots.
- Fix: substitute tokens immediately after copy, unconditionally.
Registration logic remains gated on auth token availability.
gitea app.ini (08-gitea.sh):
- REPLACE_SERVER_NAME and REPLACE_SERVER_PROTO had no matching env
vars — the script used HOSTNAME and SERVICE_PROTOCOL instead, so
__initialize_replace_variables left those tokens unsubstituted.
- Fix: export SERVER_NAME="${DOMAIN:-$HOSTNAME}" and
SERVER_PROTO="${SERVICE_PROTOCOL:-http}" as aliases after the
HOSTNAME chain is resolved.
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: add SERVER_NAME and SERVER_PROTO aliases for token substitution
- rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh: move REPLACE_ substitution outside registration guard
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh
HOSTNAME resolution now tries hostname -f (FQDN) before falling back
to the short $HOSTNAME, giving a usable ROOT_URL out of the box on
hosts where the FQDN is set in /etc/hosts or DNS.
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: HOSTNAME fallback
chain: GITEA_SERVER → GITEA_HOSTNAME → FULL_DOMAIN_NAME →
hostname -f → $HOSTNAME
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
Two issues with existing persistent volumes:
1. Deprecated app.ini settings ([cors].X_FRAME_OPTIONS,
[picture].DISABLE_GRAVATAR, [picture].ENABLE_FEDERATED_AVATAR) were
baked into the volume config from the old template and never removed
on subsequent container starts.
2. ROOT_URL/DOMAIN/SSH_DOMAIN were substituted once on first run from
the container hostname and never updated when GITEA_SERVER/GITEA_PROTO
env vars changed, causing the "Mismatched ROOT_URL" warning.
Fix: add migration + dynamic resync in __update_conf_files (08-gitea.sh)
that runs on every startup against both the persistent (/config/gitea)
and runtime (/etc/gitea) copies of app.ini, so changes take effect
immediately without a second restart.
Set GITEA_PROTO=https and GITEA_SERVER=git.casjay.work to populate
the correct ROOT_URL at container start.
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: add dynamic ROOT_URL
resync and deprecated-setting removal to __update_conf_files
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
Fixes three bugs discovered during live container testing.
The critical bug was a bash post-increment no-op: `exitCode=$((exitCode++))`
assigns the *old* value back to the variable, so exitCode stays 0 even
when a download fails. This caused the Docker build to succeed silently
when the gitea binary download failed, publishing a broken image to Docker Hub.
- rootfs/root/docker/setup/05-custom.sh: change exitCode=$((exitCode++)) to
exitCode=$((exitCode + 1)) in both the gitea and act_runner failure handlers
- rootfs/usr/local/bin/entrypoint.sh: change CONTAINER_NAME and description
from "archlinux" (copied template default) to "gitea"
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: remove leading space from
[ -d " /config/ssh" ] path test so the directory existence check is correct
.claude/
rootfs/root/docker/setup/05-custom.sh
rootfs/usr/local/bin/entrypoint.sh
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
Three more bugs found during full component verification:
1. RUNNER_CONFIG_DEFAULT pointed to config.yaml but the actual
template filename is default_config.yaml — gitea named runner
never registered because the file-existence check always failed.
2. CACHE_CONFIG_FILE pointed to cache.yaml but the actual template
filename is cache_server.yaml — cache-server launch always
skipped silently.
3. act_runner v1.0.6 requires cache.external_secret on both the
cache-server and any runner using external_server; neither config
had it, so cache-server exited immediately and runner registration
failed when external_server was present in default_config.yaml.
4. Registration log redirect was 2>/dev/stdout >>"$log" (stderr went
to original stdout, not the log); corrected to >>"$log" 2>&1.
- rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh:
- RUNNER_CONFIG_DEFAULT: config.yaml → default_config.yaml
- CACHE_CONFIG_FILE: cache.yaml → cache_server.yaml
- fix registration log redirect: >>"$RUNNER_LOG_FILE" 2>&1
- rootfs/tmp/etc/act_runner/default_config.yaml: remove
external_server (no shared secret configured; each runner uses
its own internal cache)
- rootfs/tmp/etc/act_runner/cache_server.yaml: set enabled: false
(cache-server requires external_secret; disabled until a proper
shared-secret setup is added)
Verified: 6 daemons on fresh start (1 gitea named + 5 runners), all
with 22 labels; clean reconnect on restart with no re-registration.
rootfs/tmp/etc/act_runner/cache_server.yaml
rootfs/tmp/etc/act_runner/default_config.yaml
rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh
Runner registration was broken by three compounding bugs:
1. Token generated at script init time (before gitea was ready)
2. gitea CLI missing --work-path/--custom-path flags, writing fatal
log messages to stdout which got captured as the token value
3. Token assignment inside a piped subshell didn't propagate back
to the parent shell, leaving SYS_AUTH_TOKEN empty at runtime
4. INSTALL_LOCK=false in app.ini template caused gitea to start in
install-wizard mode, making generate-runner-token always fail
- rootfs/tmp/etc/gitea/app.ini: set INSTALL_LOCK=true so gitea
auto-initializes the SQLite DB on first run
- rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh:
- defer SYS_AUTH_TOKEN: initialize to empty at global scope,
generate in __run_pre_execute_checks after gitea is confirmed up
- add --work-path/--custom-path to gitea CLI call; filter output
with grep -oE '[A-Za-z0-9]{20,}' to extract only the token
- guard token generation on INSTALL_LOCK=true in app.ini
- read token back from $CONF_DIR/tokens/system after the piped
__run_pre_execute_checks call returns (subshell escape)
Tested: fresh start registers all 5 runners with full 22-label set;
restart skips re-registration and reconnects all 5 daemons cleanly.
rootfs/tmp/etc/gitea/app.ini
rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh
When su_exec is empty (service runs as root, no user-switching needed),
printf '%q ' $su_exec expands to the literal string '' which gets embedded
in the generated start script as a command prefix, causing bash to try
executing a program named '' and failing immediately. Also add explicit
PATH and HOME exports to the RESET_ENV=no generated script so services
are not dependent on environment inheritance.
- rootfs/usr/local/etc/docker/init.d/05-dockerd.sh: fix _q_su assignment
in both RESET_ENV branches to use ${su_exec:+...} so it's empty string
(not '') when su_exec is empty; fix format string %s%s (no space between
su and cmd, su already carries trailing space); add PATH and HOME exports
to RESET_ENV=no generated script
- rootfs/usr/local/etc/docker/init.d/08-gitea.sh: same fixes
- rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh: same fixes
rootfs/usr/local/etc/docker/init.d/05-dockerd.sh
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh
Was added as a reference artifact during framework migration work
and should not live in the repo.
- rootfs/usr/local/etc/docker/init.d/00-server01.sh: deleted
rootfs/usr/local/etc/docker/init.d/00-server01.sh
Fix all undefined variable references and logic errors that would
prevent runner registration and daemon startup from working.
- rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh: fix GITEA_USER
typo (missing $ before SERVICE_USER); define 9 previously undefined
variables: RUNNER_IP_ADDRESS, RUNNER_CONFIG_DEFAULT, RUNNER_DEFAULT_HOME,
RUNNER_CONFIG_NAME, RUNNER_LOG_FILE, RUNNER_DAEMON_LOG, RUNNER_CACHE_HOST,
CACHE_CONFIG_FILE, CACHE_LOG_FILE; swap ctime/waitTime local declarations
so ctime is defined before waitTime uses it; quote act_runner daemon and
cache-server --config paths
rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh
Update the embedded entrypoint copies in rootfs/ to match the
upstream template change. Internal state files renamed to dotfiles
so they're not matched by `/run/*.pid` cleanup globs:
- /run/init.d/entrypoint.pid -> /run/.entrypoint.pid
- /run/no_exit.pid -> /run/.no_exit.pid
- /run/backup.pid -> /run/.backup.pid
- /run/__start_init_scripts.pid -> /run/.start_init_scripts.pid
Per-service PIDs in /run/init.d/ are unchanged.
rootfs/usr/local/bin/entrypoint.sh
rootfs/usr/local/etc/docker/functions/entrypoint.sh
rootfs/usr/local/etc/docker/init.d/05-dockerd.sh
rootfs/usr/local/etc/docker/init.d/08-gitea.sh
rootfs/usr/local/etc/docker/init.d/zz-act_runner.sh
rootfs/usr/local/share/template-files/config/env/default.sample
rootfs/usr/local/share/template-files/config/env/examples/zz-entrypoint.sh