# Docker Container Repository Specification ## Repository Overview **Repository:** casjaysdevdocker **Maintainer:** CasjaysDev **License:** WTFPL / MIT **Purpose:** Collection of containerized applications using standardized Alpine-based templates --- ## Repository Structure This repository contains multiple Docker container projects organised as subdirectories. Each project follows a consistent structure and naming convention. ### Directory Layout ``` casjaysdevdocker/ ├── ampache/ # Media streaming server ├── apprise/ # Notification service ├── aria2/ # Download manager ├── bind/ # DNS server ├── bun/ # JavaScript runtime ├── caddy/ # Web server ├── code/ # VS Code server ├── couchdb/ # NoSQL database ├── ddns/ # Dynamic DNS client ├── deno/ # JavaScript runtime ├── docker/ # Docker-in-Docker ├── enclosed/ # Encrypted note sharing ├── forgejo/ # Git forge ├── gitea/ # Git service ├── gotify/ # Push notification server ├── i2pd/ # I2P router ├── icecast/ # Audio streaming ├── ifconfig/ # IP info service ├── jekyll/ # Static site generator ├── lighttpd/ # Web server ├── mailman/ # Mailing list manager ├── mariadb/ # SQL database ├── mongodb/ # NoSQL database ├── mpd/ # Music player daemon ├── mysql/ # SQL database ├── navidrome/ # Music streaming ├── neovim/ # Text editor ├── nextcloud/ # Cloud storage ├── nginx/ # Web server ├── nodejs/ # Node.js runtime ├── ntfy/ # Notification service ├── ollama/ # LLM runtime ├── opencloud/ # Cloud platform ├── opengist/ # Gist clone ├── pastebin/ # Pastebin service ├── php/ # PHP runtime ├── podman/ # Container runtime ├── postfix/ # Mail server ├── postgres/ # SQL database ├── proftpd/ # FTP server ├── python/ # Python runtime ├── redis/ # In-memory database ├── soft-serve/ # Git server ├── sqlite/ # SQL database ├── ssl-ca/ # SSL certificate authority ├── tftpd/ # TFTP server ├── tor/ # Tor relay ├── traefik/ # Reverse proxy ├── transmission/ # BitTorrent client ├── valkey/ # Redis fork ├── vim/ # Text editor ├── webmin/ # Web admin interface ├── wordpress/ # CMS platform ├── wttr/ # Weather service ├── xfce4/ # Desktop environment ├── ympd/ # MPD web client ├── youtube-dl/ # Video downloader └── tools/ # Shared tools & scripts ``` --- ## Standard Project Structure Each project directory contains: ``` project-name/ ├── .dockerignore # [generated] Docker build exclusions ├── .env.scripts # [generated] Environment variables for gen-dockerfile ├── .git/ # Git repository ├── .gitattributes # [generated] Git attributes ├── .gitea/workflows/ │ ├── docker.yaml # [hand-crafted] Legacy CI workflow — do not overwrite │ └── build.yml # [generated] by gen-dockerfile actions — regenerate freely ├── .gitignore # [generated] Git ignore patterns ├── Dockerfile # [generated] Container build definition ├── LICENSE.md # License information ├── README.md # [generated] Project documentation └── rootfs/ # Container filesystem overlay ├── root/docker/setup/ # [generated] Build-time setup scripts │ ├── 00-init.sh │ ├── 01-system.sh │ ├── 02-packages.sh │ ├── 03-files.sh │ ├── 04-users.sh │ ├── 05-custom.sh │ ├── 06-post.sh │ └── 07-cleanup.sh └── usr/local/ ├── bin/ │ ├── entrypoint.sh # [generated] Container entrypoint │ └── pkmgr # [generated] Package manager wrapper ├── etc/docker/ │ ├── functions/ │ │ └── entrypoint.sh # [generated] Entrypoint functions │ └── init.d/ # [hand-crafted] Runtime init scripts (one per service) └── share/template-files/ # [hand-crafted] Config/data templates ├── config/ ├── data/ └── defaults/ ``` **`[generated]`** — safe to overwrite with `gen-dockerfile --update`; changes will be lost on next regeneration. **`[hand-crafted]`** — must not be overwritten by gen-dockerfile; customised per container. --- ## gen-dockerfile Tool All containers are built using the **gen-dockerfile** tool (`bin/gen-dockerfile` in casjay-dotfiles). It generates standardised Dockerfiles, `.env.scripts`, workflow YAML, `pkmgr` scripts, and `rootfs/` scaffolding. ### Usage ```bash gen-dockerfile [options] [template] [Dockerfile] gen-dockerfile --dir ./myapp alpine gen-dockerfile --dir ./myapp --nginx --tag 1.25 gen-dockerfile --dir ./existing-project --update gen-dockerfile --dir ./myapp actions ``` ### Templates (positional argument or `--template`) | Template | Description | Pull source (build stage) | |----------|-------------|--------------------------| | `alpine` | Alpine Linux (default) | `alpine` (Docker Hub official) | | `arch` / `archlinux` | Arch Linux ARM | `menci/archlinuxarm` | | `debian` | Debian | `debian` | | `ubuntu` | Ubuntu | `ubuntu` | | `rhel` / `almalinux` / `rockylinux` / `centos` / `oraclelinux` / `redhat` | AlmaLinux/RHEL family | `almalinux` | | `web` | Debian-based build stage + xorg + web packages; final image is scratch | Debian | | `xorg` | Debian-based build stage + xorg packages; final image is scratch | Debian | | `scratch` | Scratch final image only (pair with any build template) | N/A | > **Note:** The pull source is the base image pulled for the **build stage**. All templates produce a `FROM scratch` final stage. The casjaysdev registry images (`casjaysdev/alpine`, `casjaysdev/debian`, etc.) are the **push/output** targets, not the pull source. ### CLI Flags | Flag | Description | |------|-------------| | `--dir ` | Working directory (default: `$PWD`) | | `--template ` | Override template type | | `--registry ` | Registry provider URL (e.g. `https://docker.io`) | | `--org <[git\|reg:]name>` | Override org/namespace — prefix `git:` or `reg:` to scope to one system, bare value sets both | | `--user <[git\|reg:]name>` | Alias for `--org` | | `--repo <[git\|reg:]name>` | Override repo/image name — same scope prefix rules | | `--tag ` | Image version tag (default: `latest`) | | `--add-tags ` | Comma-separated additional tags (or `USE_DATE` for auto date tag) | | `--distro-name ` | Base image pull URL (overrides `ENV_PULL_URL`) | | `--distro-version ` | Base image tag (overrides `ENV_DISTRO_TAG`) | | `--startup` | Create a container start script only | | `--nogit` | Skip `git init` in the new project dir | | `--dockerfile` | Regenerate Dockerfile only — skip other files | | `--update` | Re-run gen-dockerfile on an existing project to regenerate files | | `--init` | Init mode | | `--x11` | Enable X11/xorg support | | `--ports ` | Additional EXPOSE ports | | `--pkmgr` | Generate `pkmgr` script only and exit | | `--apache` | Add Apache2 packages + setup | | `--nginx` | Add nginx packages + setup | | `--mysql` / `--mariadb` | Add MariaDB packages + setup | | `--postgres` | Add PostgreSQL packages + setup | | `--php` | Add PHP packages + setup | | `--application ` | Add a templatemgr application install step | | `--alpine` / `--almalinux` / `--archlinux` / `--debian` / `--ubuntu` | Shortcut distro flags | | `--force` | Overwrite existing files without prompting | | `--silent` | Suppress non-error output | | `--debug` | Enable `set -x` tracing | | `--no-color` | Disable colour output | ### Special Subcommands ```bash gen-dockerfile --dir ./myapp actions ``` Creates `.gitea/workflows/build.yml` (or `build.$version.yml` for versioned tags) from the existing `Dockerfile`. The example workflow in `.github/example/.gitea/workflows/docker.yaml` is the canonical reference pattern for the legacy hand-written style; `gen-dockerfile actions` generates `build.yml` with the same structure. ### Template Resolution Templates are loaded from the first match: 1. `$GEN_DOCKERFILE_CONFIG_DIR/templates/.template` (user override) 2. `$CASJAYSDEVDIR/templates/dockerfiles/.template` (installed scripts tree) `template_options.source` is sourced after `__set_variables` runs, allowing template-specific variable overrides. --- ## `.env.scripts` File Generated at project root; sourced by gen-dockerfile and by the CI workflow at build time. ### Current Variable Names ```bash # - - - - - - - - - - - - - - - - - - - - - - - - - ##@Version : 202601291955-git # ... # - - - - - - - - - - - - - - - - - - - - - - - - - # Entrypoint Settings DOCKER_ENTYPOINT_PORTS_WEB="${DOCKER_ENTYPOINT_PORTS_WEB}" DOCKER_ENTYPOINT_PORTS_SRV="${DOCKER_ENTYPOINT_PORTS_SRV}" DOCKER_ENTYPOINT_HEALTH_APPS="$DOCKER_ENTYPOINT_HEALTH_APPS" DOCKER_ENTYPOINT_HEALTH_ENDPOINTS="$DOCKER_ENTYPOINT_HEALTH_ENDPOINTS" # - - - - - - - - - - - - - - - - - - - - - - - - - # Dockerfile info ENV_DOCKERFILE="Dockerfile" # ENV_REGISTRY_REPO: Registry repository/image name ENV_REGISTRY_REPO="myapp" ENV_USE_TEMPLATE="alpine" # - - - - - - - - - - - - - - - - - - - - - - - - - # Maintainer info ENV_REGISTRY_ORG="casjaysdevdocker" ENV_VENDOR="CasjaysDev" ENV_AUTHOR="CasjaysDev" ENV_MAINTAINER="CasjaysDev " # - - - - - - - - - - - - - - - - - - - - - - - - - # Repository URLs (Full URLs) # ENV_GIT_REPO_URL: Complete Git repository URL for source code ENV_GIT_REPO_URL="https://github.com/casjaysdevdocker/myapp" # ENV_REGISTRY_URL: Registry provider base URL ENV_REGISTRY_URL="https://docker.io" # - - - - - - - - - - - - - - - - - - - - - - - - - # Push Configuration # ENV_REGISTRY_PUSH: Complete push destination (registry/org/repo) ENV_REGISTRY_PUSH="casjaysdevdocker/myapp" # ENV_IMAGE_TAG: Default tag for the image ENV_IMAGE_TAG="latest" # ENV_ADD_TAGS: Additional tags, comma-separated (USE_DATE = auto date tag) ENV_ADD_TAGS="" # - - - - - - - - - - - - - - - - - - - - - - - - - # Additional push destinations (if needed) ENV_ADD_IMAGE_PUSH="" # - - - - - - - - - - - - - - - - - - - - - - - - - # Pull Configuration # ENV_PULL_URL: Source image to pull from (base image) ENV_PULL_URL="alpine" # ENV_DISTRO_TAG: Tag for the pull source image ENV_DISTRO_TAG="${IMAGE_VERSION}" # - - - - - - - - - - - - - - - - - - - - - - - - - # Port exposure SERVICE_PORT="" EXPOSE_PORTS="" # - - - - - - - - - - - - - - - - - - - - - - - - - # Language runtime version (go, php, rust, ruby, etc) LANG_VERSION="" # - - - - - - - - - - - - - - - - - - - - - - - - - # Runtime versions PHP_VERSION="system" NODE_VERSION="system" NODE_MANAGER="system" # - - - - - - - - - - - - - - - - - - - - - - - - - # Default directories WWW_ROOT_DIR="${WWW_ROOT_DIR:-/usr/local/share/httpd/default}" # - - - - - - - - - - - - - - - - - - - - - - - - - ENV_PACKAGES="" ``` ### Variable Reference | Variable | Purpose | |----------|---------| | `ENV_REGISTRY_REPO` | Image name in the registry | | `ENV_REGISTRY_ORG` | Organisation/namespace in the registry | | `ENV_REGISTRY_URL` | Registry base URL (e.g. `https://docker.io`) | | `ENV_REGISTRY_PUSH` | Full push path `org/repo` (registry host prepended if not `docker.io`) | | `ENV_ADD_IMAGE_PUSH` | Extra push destinations | | `ENV_GIT_REPO_URL` | Full Git repository URL | | `ENV_USE_TEMPLATE` | Template name to use (alpine, debian, etc.) | | `ENV_PULL_URL` | Base image to pull from | | `ENV_DISTRO_TAG` | Tag for the base image | | `ENV_IMAGE_TAG` | Default image tag (default: `latest`) | | `ENV_ADD_TAGS` | Additional comma-separated tags; `USE_DATE` auto-generates a `YYMM` tag | | `ENV_PACKAGES` | Space-separated package list to install | | `ENV_VENDOR` / `ENV_AUTHOR` / `ENV_MAINTAINER` | Label metadata | | `SERVICE_PORT` | Primary exposed port | | `EXPOSE_PORTS` | Additional exposed ports | | `LANG_VERSION` | Language runtime version (go, php, rust, ruby, etc.) | | `PHP_VERSION` / `NODE_VERSION` / `NODE_MANAGER` | Runtime versions | | `WWW_ROOT_DIR` | Web root directory (default: `/usr/local/share/httpd/default`) | | `DOCKER_ENTYPOINT_PORTS_WEB` | Web ports passed to entrypoint | | `DOCKER_ENTYPOINT_PORTS_SRV` | Service ports passed to entrypoint | | `DOCKER_ENTYPOINT_HEALTH_APPS` | Apps to health-check | | `DOCKER_ENTYPOINT_HEALTH_ENDPOINTS` | Endpoints to health-check | ### Legacy Variable Auto-Migration `gen-dockerfile --update` (and on every run) calls `__migrate_env_script` which automatically renames old variable names to the current ones. Old names that are no longer used: | Old Name | Current Name | |----------|-------------| | `ENV_IMAGE_NAME` | `ENV_REGISTRY_REPO` | | `ENV_IMAGE_PUSH` | `ENV_REGISTRY_PUSH` | | `ENV_HUB_BASE` | `ENV_REGISTRY_URL` | | `ENV_ORG_NAME` | `ENV_REGISTRY_ORG` | Never use the old names in new `.env.scripts` files. --- ## Dockerfile Template Structure ### Build Arguments Top-of-file ARGs (before any `FROM`): | Argument | Description | Default | |----------|-------------|---------| | `IMAGE_NAME` | Container name | (from `ENV_REGISTRY_REPO`) | | `PHP_SERVER` | PHP server name | (same as `IMAGE_NAME`) | | `BUILD_DATE` | Build timestamp | YYYYMMDDHHMM | | `LANGUAGE` | System locale | `en_US.UTF-8` | | `TIMEZONE` | System timezone | `America/New_York` | | `WWW_ROOT_DIR` | Web root | `/usr/local/share/httpd/default` | | `PATH` | System `PATH` | `/usr/local/etc/docker/bin:/usr/local/sbin:...` | | `USER` | Build user | `root` | | `SHELL_OPTS` | Shell options | `set -e -o pipefail` | | `SERVICE_PORT` | Primary service port | (from `.env.scripts`) | | `EXPOSE_PORTS` | Additional ports | (from `.env.scripts`) | | `PHP_VERSION` | PHP version | `system` | | `NODE_VERSION` | Node.js version | `system` | | `NODE_MANAGER` | Node manager | `system` | | `IMAGE_REPO` | Full push path `org/repo` | (computed) | | `IMAGE_VERSION` | Image version | `latest` | | `CONTAINER_VERSION` | Additional image tags | (computed) | | `PULL_URL` | Base image to pull | `alpine` (official Docker Hub) | | `DISTRO_VERSION` | Base image tag | `${IMAGE_VERSION}` | | `BUILD_VERSION` | Build version alias | `${BUILD_DATE}` | Stage 2 (`FROM scratch`) additional ARGs: | Argument | Description | |----------|-------------| | `TZ` | Timezone alias | | `GIT_COMMIT` | Git SHA at build time (from CI) | | `LICENSE` | Image license | default `WTFPL` | | `ENV_PORTS` | Alias for `${EXPOSE_PORTS}` | ### Generated Dockerfile Structure #### Stage 1: Build Stage ```dockerfile FROM tianon/gosu:latest AS gosu FROM ${PULL_URL}:${DISTRO_VERSION} AS build # ... ARG declarations ... ENV ENV=~/.profile ENV SHELL="/bin/sh" ENV PATH="${PATH}" ENV TZ="${TIMEZONE}" ENV TIMEZONE="${TZ}" ENV LANG="${LANGUAGE}" ENV TERM="xterm-256color" ENV HOSTNAME="casjaysdevdocker-${IMAGE_NAME}" COPY ./rootfs/. / RUN pkmgr update; pkmgr install bash ENV SHELL="/bin/bash" SHELL ["/bin/bash", "-c"] COPY --from=gosu /usr/local/bin/gosu /usr/local/bin/gosu # Setup scripts run in order: # 00-init.sh - Initialize base system # 01-system.sh - Configure system settings # 02-packages.sh - Install/configure packages (after PACK_LIST install) # 03-files.sh - Copy/modify files # 04-users.sh - Create users/groups # 05-custom.sh - Custom application setup # 06-post.sh - Post-installation tasks # 07-cleanup.sh - Clean up temporary files ``` #### Stage 2: Final Image ```dockerfile FROM scratch # ... ARG and LABEL declarations ... ENV ENV=~/.bashrc ENV USER="${USER}" ENV PATH="${PATH}" ENV TZ="${TIMEZONE}" ENV SHELL="/bin/bash" ENV TIMEZONE="${TZ}" ENV LANG="${LANGUAGE}" ENV TERM="xterm-256color" ENV PORT="${SERVICE_PORT}" ENV ENV_PORTS="${ENV_PORTS}" ENV CONTAINER_NAME="${IMAGE_NAME}" ENV HOSTNAME="casjaysdev-${IMAGE_NAME}" ENV PHP_SERVER="${PHP_SERVER}" ENV NODE_VERSION="${NODE_VERSION}" ENV NODE_MANAGER="${NODE_MANAGER}" ENV PHP_VERSION="${PHP_VERSION}" ENV DISTRO_VERSION="${IMAGE_VERSION}" ENV WWW_ROOT_DIR="${WWW_ROOT_DIR}" COPY --from=build /. / VOLUME [ "/config", "/data" ] EXPOSE ${SERVICE_PORT} ${ENV_PORTS} STOPSIGNAL SIGRTMIN+3 ENTRYPOINT [ "tini", "-p", "SIGTERM", "--", "/usr/local/bin/entrypoint.sh" ] HEALTHCHECK --start-period=10m --interval=5m --timeout=15s \ CMD [ "/usr/local/bin/entrypoint.sh", "healthcheck" ] ``` ### Container Labels All containers include OpenContainers standard labels: ```dockerfile LABEL maintainer="CasjaysDev " LABEL org.opencontainers.image.vendor="CasjaysDev" LABEL org.opencontainers.image.authors="CasjaysDev" LABEL org.opencontainers.image.description="Containerized version of ${IMAGE_NAME}" LABEL org.opencontainers.image.title="${IMAGE_NAME}" LABEL org.opencontainers.image.base.name="${IMAGE_NAME}" LABEL org.opencontainers.image.authors="${LICENSE}" LABEL org.opencontainers.image.created="${BUILD_DATE}" LABEL org.opencontainers.image.version="${BUILD_VERSION}" LABEL org.opencontainers.image.schema-version="${BUILD_VERSION}" LABEL org.opencontainers.image.url="https://hub.docker.com/r/casjaysdevdocker/${IMAGE_NAME}" LABEL org.opencontainers.image.source="https://hub.docker.com/r/casjaysdevdocker/${IMAGE_NAME}" LABEL org.opencontainers.image.vcs-type="Git" LABEL org.opencontainers.image.revision="${GIT_COMMIT}" LABEL org.opencontainers.image.source="https://github.com/casjaysdevdocker/${IMAGE_NAME}" LABEL org.opencontainers.image.documentation="https://github.com/casjaysdevdocker/${IMAGE_NAME}" LABEL com.github.containers.toolbox="false" ``` > **Note:** `org.opencontainers.image.source` appears twice intentionally — first set to the Docker Hub URL, then overridden to the Git source URL. The second value wins at runtime. --- ## Common Package List Standard packages included in most containers (Alpine template): ``` bash-completion git curl wget sudo unzip iproute2 ssmtp openssl jq tzdata mailcap ncurses util-linux pciutils usbutils coreutils binutils findutils grep rsync zip tini py3-pip procps net-tools sed gawk attr readline lsof less shadow ca-certificates ``` --- ## Setup Scripts Convention Build-time scripts in `rootfs/root/docker/setup/` run inside the Docker build layer. ### 00-init.sh - Initialize base directory structure - Set up template directories - Create initial environment ### 01-system.sh - Configure APK/apt/yum repositories - Set up system settings - Configure locales and timezone ### 02-packages.sh - Install application-specific packages - Configure package managers (pip, npm, etc.) - Set up language runtimes ### 03-files.sh - Copy configuration files - Set file permissions - Create symlinks ### 04-users.sh - Create application users - Set up user directories - Configure user permissions ### 05-custom.sh - Application-specific installation - Download/compile software - Custom configuration ### 06-post.sh - Post-installation configuration - Initialize databases - Generate default configs ### 07-cleanup.sh - Remove build dependencies - Clean package caches - Remove temporary files --- ## Entrypoint System ### Entrypoint Script Flow ``` /usr/local/bin/entrypoint.sh ├─ Load functions from /usr/local/etc/docker/functions/entrypoint.sh ├─ Set up environment variables (sources /root/env.sh, /usr/local/etc/docker/env/*.sh, /config/env/*.sh) ├─ Create /config and /data volumes ├─ Run ALL init scripts from /usr/local/etc/docker/init.d/ (sorted order) via __start_init_scripts ├─ Handle healthcheck command └─ Execute main application ``` ### Init.d Scripts — CRITICAL RULES **Each service in a repo gets its own numbered init.d script. Never merge or remove them.** - `__start_init_scripts` iterates and sources **every** `*.sh` file in `init.d/` in sort order. - Multi-process repos have **one script per service**. Example for a repo running three daemons: ``` init.d/01-named.sh — BIND/named DNS server init.d/02-nginx.sh — nginx web front-end init.d/03-php-fpm.sh — PHP-FPM for web UI ``` - **Migration task = UPDATE each script to the canonical pattern, never delete services.** - The canonical pattern is `.github/example/rootfs/usr/local/etc/docker/init.d/04-example.sh`. ### Required Variables in Every Init.d Script ```bash SERVICE_NAME="myapp" # used for PID files, log dirs, config dirs EXEC_CMD_BIN='myapp' # daemon binary (single-quoted — expanded later) EXEC_CMD_ARGS='' # daemon arguments (single-quoted) EXEC_PRE_SCRIPT='' # pre-start script (single-quoted) SERVICE_USES_PID='' # '' for long-running daemons; 'no' for config-only steps IS_WEB_SERVER="no" # 'yes' sets RESET_ENV and default port 80 IS_DATABASE_SERVICE="no" USES_DATABASE_SERVICE="no" DATABASE_SERVICE_TYPE="sqlite" # custom|sqlite|redis|postgres|mariadb|mysql|couchdb|mongodb|supabase RUNAS_USER="root" ``` ### Required Hook Functions in Every Init.d Script All eight must be defined (even if they just `return 0`): ```bash __run_precopy() # runs before copying /config to /etc __execute_prerun() # runs before service starts __run_pre_execute_checks() # validation before exec; non-zero aborts start __update_conf_files() # update config files (sed replacements, etc.) __pre_execute() # final setup before exec __post_execute() # background post-start tasks __pre_message() # message to show before exec __update_ssl_conf() # SSL certificate setup __create_service_env() # generates /config/env/$SERVICE_NAME.sh template __run_start_script() # builds and runs the exec script __run_secure_function() # chmod 600 on credential files ``` ### PID Sentinel Check (CRITICAL) Every init.d script **must** guard on the correct sentinel file: ```bash # Correct — dot prefix, no double underscore if [ ! -f "/run/.start_init_scripts.pid" ]; then echo "__start_init_scripts function hasn't been Initialized" >&2 SERVICE_IS_RUNNING="no" __script_exit 1 fi ``` The file is `/run/.start_init_scripts.pid` — a leading dot, no underscores in the filename portion. Any other form silently skips the guard. ### Directory Variables ```bash DATA_DIR="/data/$SERVICE_NAME" # persistent data CONF_DIR="/config/$SERVICE_NAME" # persistent config ETC_DIR="/etc/$SERVICE_NAME" # runtime etc (synced from CONF_DIR) LOG_DIR="/data/logs/$SERVICE_NAME" # logs TMP_DIR="/tmp/$SERVICE_NAME" RUN_DIR="/run/$SERVICE_NAME" ROOT_FILE_PREFIX="/config/secure/auth/root" USER_FILE_PREFIX="/config/secure/auth/user" ``` ### Volume Mounts - `/config` — Persistent configuration files - `/data` — Persistent application data --- ## CI/CD Integration ### Two Workflow Files — Hand-Crafted vs Generated | File | Origin | Never overwrite? | |------|--------|-----------------| | `.gitea/workflows/docker.yaml` | Hand-crafted legacy file kept in `.github/example/` | Yes — do not overwrite | | `.gitea/workflows/build.yml` | Generated by `gen-dockerfile actions` | No — regenerate freely | ### Generated Workflow (`gen-dockerfile actions`) `gen-dockerfile actions` writes `.gitea/workflows/build.yml` (or `build..yml` for versioned tags) from the current `Dockerfile`. All actions are SHA-pinned. **Triggers:** `push` to `main`, monthly schedule (`cron: '0 2 1 * *'`), `workflow_dispatch`. **Registry strategy:** - Always logs in to the Gitea registry using the auto-provided `GITEA_TOKEN`. - Conditionally logs in to Docker Hub (or another registry) when `vars.DOCKER_USERNAME` is set; credentials via `vars.DOCKER_USERNAME` + `secrets.DOCKER_PASSWORD`. - `vars.DOCKER_REGISTRY` overrides the registry URL (default: `docker.io`). - `vars.DOCKER_ORG` overrides the namespace (fallback: `vars.DOCKER_USERNAME`). **`build-args` passed:** only `BUILD_DATE`, `GIT_COMMIT`, `BUILD_VERSION` — not `TIMEZONE`, `LANGUAGE`, `LICENSE`, or `TZ`. **Tags pushed:** `` and `latest` to both Gitea and (if configured) Docker Hub. ```yaml name: Build and Push on: push: branches: [main] schedule: - cron: '0 2 1 * *' workflow_dispatch: jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Set up QEMU uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - name: Compute build metadata id: meta run: | echo "build_date=$(date -u +%Y%m%d%H%M)" >> "$GITHUB_OUTPUT" echo "tag_yymm=$(date -u +%y%m)" >> "$GITHUB_OUTPUT" echo "git_commit=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" echo "registry_host=$(echo '${{ github.server_url }}' | sed 's|https://||')" >> "$GITHUB_OUTPUT" - name: Login to Gitea registry uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ${{ steps.meta.outputs.registry_host }} username: ${{ github.repository_owner }} password: ${{ secrets.GITEA_TOKEN }} - name: Login to Docker Hub if: vars.DOCKER_USERNAME != '' uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ${{ vars.DOCKER_REGISTRY || 'docker.io' }} username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.meta.outputs.registry_host }}/${{ github.repository }}:${{ steps.meta.outputs.tag_yymm }} ${{ vars.DOCKER_USERNAME != '' && format('{0}/{1}/{2}:{3}', vars.DOCKER_REGISTRY || 'docker.io', vars.DOCKER_ORG || vars.DOCKER_USERNAME, github.event.repository.name, steps.meta.outputs.tag_yymm) || '' }} ${{ steps.meta.outputs.registry_host }}/${{ github.repository }}:latest ${{ vars.DOCKER_USERNAME != '' && format('{0}/{1}/{2}:{3}', vars.DOCKER_REGISTRY || 'docker.io', vars.DOCKER_ORG || vars.DOCKER_USERNAME, github.event.repository.name, 'latest') || '' }} build-args: | BUILD_DATE=${{ steps.meta.outputs.build_date }} GIT_COMMIT=${{ steps.meta.outputs.git_commit }} BUILD_VERSION=${{ steps.meta.outputs.tag_yymm }} annotations: | org.opencontainers.image.created=${{ steps.meta.outputs.build_date }} org.opencontainers.image.version=latest org.opencontainers.image.revision=${{ steps.meta.outputs.git_commit }} org.opencontainers.image.title=${{ github.event.repository.name }} org.opencontainers.image.description=Containerized version of ${{ github.event.repository.name }} org.opencontainers.image.vendor=CasjaysDev org.opencontainers.image.authors=CasjaysDev org.opencontainers.image.licenses=WTFPL org.opencontainers.image.url=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.documentation=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.vcs-type=Git com.github.containers.toolbox=false ``` For versioned builds (`gen-dockerfile actions` on a tagged version), the workflow is named `Build and Push `, omits the schedule trigger, and pushes only that fixed version tag. ### Hand-Crafted Workflow (`.gitea/workflows/docker.yaml`) The legacy `docker.yaml` in `.github/example/` is hand-crafted and kept for reference. It differs from the generated workflow: uses `catthehacker/ubuntu:act-latest` container, tag-pinned actions (`@v2`/`@v3`/`@v4`), `secrets.DOCKER_USERNAME` + `secrets.DOCKER_TOKEN`, and the old `steps.meta.outputs.*` pattern. Do not use this as a template for new containers — use `gen-dockerfile actions` instead. --- ## Package Manager Wrapper (pkmgr) The `pkmgr` script in `rootfs/usr/local/bin/pkmgr` provides a unified interface: ```bash pkmgr update # Update package lists pkmgr install # Install packages pkmgr remove # Remove packages pkmgr clean # Clean package cache ``` Automatically detects and uses the appropriate package manager (apk, apt-get, dnf, pacman). --- ## Development Workflow ### Creating a New Container ```bash # 1. Generate scaffolding gen-dockerfile --dir ./myapp alpine # 2. Customise .env.scripts (set ENV_REGISTRY_REPO, ENV_REGISTRY_ORG, ports, etc.) # 3. Customise setup scripts in rootfs/root/docker/setup/ # 4. Add init.d runtime script (copy from .github/example) # 5. Build and test docker build -t myapp . docker run -it myapp # 6. Commit and push to trigger CI/CD ``` ### Updating an Existing Container ```bash # Regenerate Dockerfile and related files from current .env.scripts gen-dockerfile --dir ./myapp --update # Regenerate workflow file only gen-dockerfile --dir ./myapp actions ``` ### Adding a New Container from Scratch 1. `gen-dockerfile --dir /path/to/new-container --nogit alpine` 2. Edit `.env.scripts` — set `ENV_REGISTRY_REPO`, `ENV_REGISTRY_PUSH`, `ENV_GIT_REPO_URL` 3. Copy `04-example.sh` from `.github/example/` as the starting point for init.d scripts 4. Add application-specific setup in `rootfs/root/docker/setup/05-custom.sh` --- ## Project Categories ### Web Servers - nginx, lighttpd, caddy ### Databases - mysql, mariadb, postgres, mongodb, couchdb, redis, valkey, sqlite ### Development Tools - code, neovim, vim, nodejs, python, php, deno, bun ### Container Tools - docker, podman ### Git Services - gitea, forgejo, soft-serve, opengist ### Media Services - ampache, navidrome, mpd, ympd, icecast, youtube-dl ### Networking - bind, traefik, tor, i2pd, ddns, ifconfig ### Communication - mailman, postfix, gotify, ntfy, apprise ### Cloud Services - nextcloud, wordpress ### Utilities - tools, transmission, aria2, tftpd, ssl-ca, wttr, xfce4, webmin --- ## Resources - **Docker Hub:** - **Git Repository:** - **gen-dockerfile Tool:** `/usr/local/bin/gen-dockerfile` (source: `casjay-dotfiles/scripts/bin/gen-dockerfile`) - **Template Source:** `casjay-dotfiles/scripts/templates/dockerfiles/` - **Example Project:** `.github/example/`