mirror of
https://github.com/casjaysdevdocker/opencloud
synced 2026-08-15 02:01:14 -04:00
📖 Standardize AI.md; move project spec to IDEA.md 📖
opencloud / release-opencloud (push) Waiting to run
opencloud / release-opencloud (push) Waiting to run
AI.md is now the standardized application-image specification from claudemgr/docker/CASJAYSDEVDOCKER.md (PARTs 0–8: critical rules, repo model, template system and OCI label canon, tooling, .env.scripts, runtime system, README layout, CI/CD, verification gates). The former AI.md — the OpenCloud project specification — moved wholesale to IDEA.md, which did not previously exist, so nothing was lost. - AI.md: replaced with the CASJAYSDEVDOCKER.md master template - IDEA.md: new — carries the OpenCloud project specification moved out of AI.md
This commit is contained in:
@@ -1,310 +1,697 @@
|
|||||||
# OpenCloud All-in-One Docker Container
|
# CasjaysDev Docker Application Image Specification (casjaysdevdocker)
|
||||||
|
|
||||||
## Overview
|
**Name**: {name}
|
||||||
|
|
||||||
This is a comprehensive all-in-one Docker container for OpenCloud, a Go-based file sync and share platform (successor to ownCloud). The container includes all integrated services required for a full-featured deployment.
|
**About this file:** This is the complete, authoritative specification for a CasjaysDev
|
||||||
|
Docker **application image** repository (`casjaysdevdocker/{name}`). It is a master
|
||||||
|
template — copied into an app image repo as that repo's `AI.md`. It is **permanent** —
|
||||||
|
never delete it from a repo that carries it.
|
||||||
|
|
||||||
## Included Services
|
**Note:** `{name}` in this file is a reference token, not setup-time replacement text. Its
|
||||||
|
value is always the repo directory basename (`basename "$PWD"`).
|
||||||
|
|
||||||
| Service | Port | Purpose |
|
**Maintenance procedure:** The bootstrap/update runbook (regenerating files after upstream
|
||||||
|---------|------|---------|
|
template changes, creating new repos) is NOT in this file — it lives in the
|
||||||
| ClamAV | Socket | Antivirus scanning for uploaded files |
|
`dockersrc-bootstrap` agent (it handles both repo families via `REPO_TYPE` detection).
|
||||||
| Apache Tika | 9998 | Full-text search and document extraction |
|
This file defines the standards that procedure enforces.
|
||||||
| Radicale | 5232 | CalDAV/CardDAV for Calendar and Contacts |
|
|
||||||
| Collabora CODE | 9980 | Document collaboration (LibreOffice Online) |
|
|
||||||
| OpenCloud | 9200 | Core file sync/share platform |
|
|
||||||
| Nginx | 80 | Reverse proxy (external-facing) |
|
|
||||||
|
|
||||||
## Architecture
|
---
|
||||||
|
|
||||||
|
# PART INDEX
|
||||||
|
|
||||||
|
| PART | Title |
|
||||||
|
|------|-------|
|
||||||
|
| 0 | Critical rules |
|
||||||
|
| 1 | Repository model & structure |
|
||||||
|
| 2 | Template system reference |
|
||||||
|
| 3 | Tooling — gen-dockerfile & gen-script |
|
||||||
|
| 4 | `.env.scripts` reference |
|
||||||
|
| 5 | Runtime system — setup scripts, entrypoint, init.d |
|
||||||
|
| 6 | README.md standard layout |
|
||||||
|
| 7 | CI/CD workflows |
|
||||||
|
| 8 | Verification & commit |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 0: CRITICAL RULES
|
||||||
|
|
||||||
|
## Org mapping
|
||||||
|
|
||||||
|
| System | Org | Example |
|
||||||
|
|--------|-----|---------|
|
||||||
|
| GitHub (source) | `casjaysdevdocker` | `https://github.com/casjaysdevdocker/{name}` |
|
||||||
|
| Docker Hub (push) | `casjaysdevdocker` | `casjaysdevdocker/{name}` |
|
||||||
|
|
||||||
|
`casjaysdevdocker` repos are **applications** (gitea, opengist, super-productivity,
|
||||||
|
ampache, aria2, …). They always build FROM the pre-built, multi-arch `casjaysdev/*` base
|
||||||
|
images — never directly from upstream distro images. The bases themselves live in the
|
||||||
|
separate `dockersrc` org (GitHub `dockersrc/{base}` → Docker Hub `casjaysdev/{base}`) —
|
||||||
|
see the base specification (`DOCKERSRC.md`).
|
||||||
|
|
||||||
|
## Non-negotiable rules
|
||||||
|
|
||||||
|
1. **`AI.md` is permanent** — never delete it from the repo.
|
||||||
|
2. **Generated files are owned by the template system** — never hand-tune content that
|
||||||
|
`gen-dockerfile` regenerates (see PART 1 ownership table); fix the upstream
|
||||||
|
`gen-dockerfile` template instead, then regenerate.
|
||||||
|
3. **Hand-crafted files are owned by the repo** — `gen-dockerfile` must never overwrite
|
||||||
|
app-specific init.d scripts, custom bin scripts, a `05-custom.sh` with real content, or
|
||||||
|
a hand-crafted README (PART 6).
|
||||||
|
4. **Removed OCI labels stay removed** (PART 2) — never re-add `base.name`,
|
||||||
|
`schema-version`, or duplicate `authors`/`source` entries.
|
||||||
|
5. **`image.url` is a browsable page** — `https://hub.docker.com/r/casjaysdevdocker/{name}`.
|
||||||
|
`docker.io` is only a registry pull host; it is never a label URL.
|
||||||
|
6. **`image.source` and `image.documentation` are the GitHub repo** —
|
||||||
|
`https://github.com/casjaysdevdocker/{name}`.
|
||||||
|
7. **One Dockerfile, one file set** — app repos build one image (`latest` + date tag);
|
||||||
|
version variants (`Dockerfile.{ver}`) belong to base repos only.
|
||||||
|
8. **Always `FROM casjaysdev/<base>`** — never pull an upstream distro image directly;
|
||||||
|
the base repos exist so every app shares one patched, multi-arch foundation.
|
||||||
|
9. **Only `root/`, `tmp/`, and `usr/` may exist at `rootfs/` top level** (PART 1).
|
||||||
|
10. **Maintenance runs through the `dockersrc-bootstrap` agent** — do not improvise the
|
||||||
|
update procedure from memory.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 1: REPOSITORY MODEL & STRUCTURE
|
||||||
|
|
||||||
|
## What an app image repo is
|
||||||
|
|
||||||
|
A `casjaysdevdocker/{name}` repo containerizes one application on top of a
|
||||||
|
`casjaysdev/*` base. It publishes a single image (`casjaysdevdocker/{name}:latest` plus a
|
||||||
|
date tag) — no per-version Dockerfile variants. The application itself is installed in
|
||||||
|
`05-custom.sh` and started by one or more init.d service scripts.
|
||||||
|
|
||||||
|
## Standard tree
|
||||||
|
|
||||||
```
|
```
|
||||||
+------------------+
|
{name}/
|
||||||
| Nginx |
|
├── AI.md # This specification (permanent)
|
||||||
| (Port 80) |
|
├── Dockerfile # [generated] single build file
|
||||||
+--------+---------+
|
├── .dockerignore # [generated]
|
||||||
|
|
├── .env.scripts # [generated] build config
|
||||||
+--------------------+--------------------+
|
├── .gitattributes # [generated]
|
||||||
| | | | |
|
├── .gitea/workflows/
|
||||||
+----v----+ +---v---+ +---v---+ +---v---+ +---v---+
|
│ └── build.yml # [generated] gen-dockerfile actions
|
||||||
|OpenCloud| |Collabora| |Radicale| | Tika | |ClamAV |
|
├── .gitignore # [generated]
|
||||||
| (9200) | | (9980) | | (5232) | |(9998)| |(sock) |
|
├── LICENSE.md # License (WTFPL / app's own license)
|
||||||
+---------+ +---------+ +--------+ +------+ +-------+
|
├── README.md # [generated*] standard layout (PART 6)
|
||||||
|
└── rootfs/ # Container filesystem overlay
|
||||||
|
├── root/docker/setup/ # [generated*] build-time setup scripts 00–07
|
||||||
|
├── tmp/ # staged files installed at build time (optional)
|
||||||
|
└── usr/local/
|
||||||
|
├── bin/ # [generated*] entrypoint.sh, pkmgr, symlink, copy,
|
||||||
|
│ # healthcheck + [hand-crafted] app-specific scripts
|
||||||
|
└── etc/docker/
|
||||||
|
├── env/ # [hand-crafted] build/runtime env fragments (optional)
|
||||||
|
├── functions/
|
||||||
|
│ └── entrypoint.sh # [generated] entrypoint function library
|
||||||
|
└── init.d/ # [hand-crafted] runtime init scripts (one per service)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Service Startup Order
|
`[generated]` — safe to regenerate; local edits will be lost.
|
||||||
|
`[generated*]` — regenerated from the template, EXCEPT files carrying repo-specific
|
||||||
|
content (`05-custom.sh` with a real body, extra bin scripts, a hand-crafted README) —
|
||||||
|
those follow the hand-crafted rules in PARTs 5 and 6.
|
||||||
|
`[hand-crafted]` — never overwritten by the template system.
|
||||||
|
|
||||||
Services start in this order to ensure dependencies are available:
|
App repos may additionally carry project files (`IDEA.md`, `CLAUDE.md`, `TODO.AI.md`)
|
||||||
|
per the global project conventions — they are repo-owned and never touched by tooling.
|
||||||
|
|
||||||
1. **01-clamav.sh** - Antivirus daemon (other services depend on scanning)
|
## rootfs top-level policy
|
||||||
2. **02-tika.sh** - Search indexing service
|
|
||||||
3. **03-radicale.sh** - Calendar/Contacts (CalDAV/CardDAV)
|
|
||||||
4. **04-collabora.sh** - Document collaboration
|
|
||||||
5. **05-opencloud.sh** - Main OpenCloud server
|
|
||||||
6. **06-nginx.sh** - Reverse proxy (last, needs all backends)
|
|
||||||
|
|
||||||
## Environment Variables
|
The only valid directories at the `rootfs/` root are `root/`, `tmp/`, and `usr/`.
|
||||||
|
Anything else is a leftover from old patterns. Migration map:
|
||||||
|
|
||||||
### Domain Configuration
|
| Old rootfs path | Correct rootfs path |
|
||||||
|
|-----------------|---------------------|
|
||||||
|
| `rootfs/etc/{path}` | `rootfs/tmp/etc/{path}` |
|
||||||
|
| `rootfs/config/{path}` | `rootfs/tmp/etc/{path}` |
|
||||||
|
| `rootfs/data/{path}` | `rootfs/tmp/var/{path}` |
|
||||||
|
| `rootfs/var/{path}` | `rootfs/tmp/var/{path}` |
|
||||||
|
| `rootfs/opt/{path}` | `rootfs/tmp/opt/{path}` |
|
||||||
|
| `rootfs/share/{path}` | `rootfs/usr/local/share/{path}` |
|
||||||
|
|
||||||
| Variable | Default | Description |
|
`rootfs/usr/local/share/template-files/` is retired — the `DEFAULT_TEMPLATE_DIR`,
|
||||||
|----------|---------|-------------|
|
`DEFAULT_FILE_DIR`, `DEFAULT_DATA_DIR`, and `DEFAULT_CONF_DIR` variables were removed
|
||||||
| `DOMAIN` | `$HOSTNAME` | Primary domain for all services |
|
from the template system; the entrypoint installs staged files from `rootfs/tmp/etc/`
|
||||||
| `OC_DOMAIN` | `${DOMAIN:-$HOSTNAME}` | OpenCloud domain (for URL generation) |
|
at container start instead.
|
||||||
| `COLLABORA_SERVER_NAME` | `collabora.${DOMAIN:-$HOSTNAME}` | Collabora server identifier for WOPI |
|
|
||||||
|
|
||||||
### URL Structure
|
## Repo type detection
|
||||||
|
|
||||||
Services are accessed via subdomains:
|
A repo is an **app** repo when no `Dockerfile.*` variant files exist:
|
||||||
- `${DOMAIN}` → OpenCloud (main application)
|
|
||||||
- `collabora.${DOMAIN}` → Collabora CODE (document editing)
|
|
||||||
- `radicale.${DOMAIN}` → Radicale (CalDAV/CardDAV)
|
|
||||||
|
|
||||||
### OpenCloud Configuration
|
|
||||||
|
|
||||||
| Variable | Default | Description |
|
|
||||||
|----------|---------|-------------|
|
|
||||||
| `ADMIN_PASSWORD` | (auto-generated) | Admin user password |
|
|
||||||
| `OC_CONFIG_DIR` | `/config/opencloud` | Configuration directory |
|
|
||||||
| `OC_DATA_DIR` | `/data/opencloud` | Data storage directory |
|
|
||||||
| `OC_LOG_LEVEL` | `info` | Logging verbosity |
|
|
||||||
| `OC_INSECURE` | `true` | Allow insecure connections |
|
|
||||||
| `PROXY_HTTP_ADDR` | `0.0.0.0:9200` | HTTP listen address |
|
|
||||||
| `PROXY_TLS` | `false` | Disable TLS (behind reverse proxy) |
|
|
||||||
|
|
||||||
### Service Integration
|
|
||||||
|
|
||||||
| Variable | Default | Description |
|
|
||||||
|----------|---------|-------------|
|
|
||||||
| `COLLABORATION_WOPI_SRC` | `http://localhost:9980` | Collabora WOPI endpoint |
|
|
||||||
| `COLLABORATION_APP_ADDR` | `http://localhost:9980` | Collabora app address |
|
|
||||||
| `SEARCH_EXTRACTOR_TYPE` | `tika` | Search extraction engine |
|
|
||||||
| `SEARCH_EXTRACTOR_TIKA_TIKA_URL` | `http://localhost:9998` | Tika server URL |
|
|
||||||
| `ANTIVIRUS_SCANNER_TYPE` | `clamav` | Antivirus scanner type |
|
|
||||||
| `ANTIVIRUS_CLAMAV_SOCKET` | `/run/clamav/clamd.sock` | ClamAV socket path |
|
|
||||||
|
|
||||||
### User Configuration
|
|
||||||
|
|
||||||
| Variable | Default | Description |
|
|
||||||
|----------|---------|-------------|
|
|
||||||
| `OPENCLOUD_USER` | `opencloud` | System user for OpenCloud |
|
|
||||||
| `OPENCLOUD_UID` | `1000` | User ID |
|
|
||||||
| `OPENCLOUD_GID` | `1000` | Group ID |
|
|
||||||
|
|
||||||
## Directory Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
/config/
|
|
||||||
├── opencloud/ # OpenCloud configuration
|
|
||||||
├── radicale/ # Radicale CalDAV/CardDAV config
|
|
||||||
├── clamav/ # ClamAV configuration
|
|
||||||
├── collabora/ # Collabora configuration
|
|
||||||
└── nginx/ # Nginx reverse proxy config
|
|
||||||
|
|
||||||
/data/
|
|
||||||
├── opencloud/ # User files and data
|
|
||||||
├── radicale/ # Calendar/Contact collections
|
|
||||||
├── clamav/ # ClamAV data
|
|
||||||
├── collabora/ # Collabora data
|
|
||||||
└── logs/ # All service logs
|
|
||||||
├── opencloud/
|
|
||||||
├── radicale/
|
|
||||||
├── clamav/
|
|
||||||
├── tika/
|
|
||||||
├── collabora/
|
|
||||||
└── nginx/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Basic Run
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d \
|
if find . -maxdepth 1 -name 'Dockerfile.*' -type f | grep -q -- .; then
|
||||||
--name opencloud \
|
REPO_TYPE="base"
|
||||||
-p 80:80 \
|
else
|
||||||
-v opencloud-config:/config \
|
REPO_TYPE="app"
|
||||||
-v opencloud-data:/data \
|
fi
|
||||||
casjaysdevdocker/opencloud:latest start
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Custom Admin Password
|
---
|
||||||
|
|
||||||
|
# PART 2: TEMPLATE SYSTEM REFERENCE
|
||||||
|
|
||||||
|
Templates ship with `gen-dockerfile`, installed at
|
||||||
|
`/usr/local/share/CasjaysDev/scripts/templates/dockerfiles/`
|
||||||
|
(`$CASJAYSDEVDIR/templates/dockerfiles/` in a dev checkout). To inspect what the current
|
||||||
|
templates produce, generate a fresh reference tree in a temp dir:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d \
|
gen-dockerfile /tmp/gen-dockerfile/{org}/{repo} {distro}
|
||||||
--name opencloud \
|
|
||||||
-p 80:80 \
|
|
||||||
-e ADMIN_PASSWORD=mysecurepassword \
|
|
||||||
-v opencloud-config:/config \
|
|
||||||
-v opencloud-data:/data \
|
|
||||||
casjaysdevdocker/opencloud:latest start
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker Compose
|
See `gen-dockerfile --help` for supported distros/types. Keep this PART in sync whenever
|
||||||
|
the templates change.
|
||||||
|
|
||||||
|
## Template inventory
|
||||||
|
|
||||||
|
The template name selects the base OS family; for an app repo the resulting pull URL is
|
||||||
|
always the matching `casjaysdev/*` image:
|
||||||
|
|
||||||
|
| Template | Final stage | Init / PID 1 | App pulls FROM |
|
||||||
|
|----------|-------------|--------------|----------------|
|
||||||
|
| `alpine.template` | `scratch.template` | tini | `casjaysdev/alpine` |
|
||||||
|
| `debian.template` | `scratch.template` | tini | `casjaysdev/debian` |
|
||||||
|
| `ubuntu.template` | `scratch.template` | tini | `casjaysdev/ubuntu` |
|
||||||
|
| `rhel.template` | `scratch.template` | tini | `casjaysdev/almalinux` |
|
||||||
|
| `archlinux.template` | `scratch.template` | tini | `casjaysdev/archlinux` (multi-arch manifest) |
|
||||||
|
| `web.template` | `systemd.template` | `/sbin/init` | `casjaysdev/web` |
|
||||||
|
| `xorg.template` | `systemd.template` | `/sbin/init` | `casjaysdev/xorg` |
|
||||||
|
|
||||||
|
Default template for app repos is `alpine` unless the application needs systemd, a GUI
|
||||||
|
stack, or a distro-specific package.
|
||||||
|
|
||||||
|
## Final-stage templates
|
||||||
|
|
||||||
|
`scratch.template` — all non-GUI templates.
|
||||||
|
- `ENTRYPOINT [ "tini", "-p", "SIGTERM","--", "/usr/local/bin/entrypoint.sh" ]`
|
||||||
|
- `STOPSIGNAL SIGRTMIN+3`
|
||||||
|
|
||||||
|
`systemd.template` — `web` and `xorg` (systemd is PID 1; tini is redundant).
|
||||||
|
- `ENTRYPOINT [ "/sbin/init" ]`
|
||||||
|
- `STOPSIGNAL SIGRTMIN+3`
|
||||||
|
- No `tini_provider` stage, no `COPY --from=tini_provider` line.
|
||||||
|
|
||||||
|
Both are identical apart from `ENTRYPOINT`. OCI labels, `ENV HOSTNAME`, and
|
||||||
|
`VOLUME`/`EXPOSE`/`HEALTHCHECK` are the same in both.
|
||||||
|
|
||||||
|
## OCI label standard
|
||||||
|
|
||||||
|
Both final-stage templates emit these labels (no others):
|
||||||
|
|
||||||
|
```
|
||||||
|
LABEL maintainer="${GEN_DOCKERFILE_MAINTAINER}"
|
||||||
|
LABEL org.opencontainers.image.vendor="${GEN_DOCKERFILE_VENDOR:-CasjaysDev}"
|
||||||
|
LABEL org.opencontainers.image.authors="${GEN_DOCKERFILE_AUTHOR:-CasjaysDev}"
|
||||||
|
LABEL org.opencontainers.image.licenses="${LICENSE}"
|
||||||
|
LABEL org.opencontainers.image.title="${IMAGE_NAME}"
|
||||||
|
LABEL org.opencontainers.image.description="Containerized version of ${IMAGE_NAME}"
|
||||||
|
LABEL org.opencontainers.image.created="${BUILD_DATE}"
|
||||||
|
LABEL org.opencontainers.image.version="${BUILD_VERSION}"
|
||||||
|
LABEL org.opencontainers.image.revision="${GIT_COMMIT}"
|
||||||
|
LABEL org.opencontainers.image.url="${GEN_DOCKERFILE_HUB_REPO}"
|
||||||
|
LABEL org.opencontainers.image.source="${GEN_DOCKERFILE_GIT_REPO}"
|
||||||
|
LABEL org.opencontainers.image.documentation="${GEN_DOCKERFILE_GIT_REPO}"
|
||||||
|
LABEL org.opencontainers.image.vcs-type="Git"
|
||||||
|
LABEL com.github.containers.toolbox="false"
|
||||||
|
```
|
||||||
|
|
||||||
|
Shell-expanded values (no `\`) are evaluated at template-render time by `gen-dockerfile`.
|
||||||
|
Dollar-escaped values (`\${...}`) become literal Docker `ARG`/`ENV` references in the
|
||||||
|
generated `Dockerfile`.
|
||||||
|
|
||||||
|
Resolved values for a `casjaysdevdocker` repo pushing to Docker Hub:
|
||||||
|
|
||||||
|
| Label | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| `url` | `https://hub.docker.com/r/casjaysdevdocker/{name}` — browsable Hub page; `gen-dockerfile` derives it from the registry host (`docker.io` → `hub.docker.com/r/`) |
|
||||||
|
| `source` | `https://github.com/casjaysdevdocker/{name}` |
|
||||||
|
| `documentation` | `https://github.com/casjaysdevdocker/{name}` |
|
||||||
|
|
||||||
|
Older app repos may still carry `url="https://docker.io/casjaysdevdocker/{name}"` — that
|
||||||
|
is the stale form; regeneration corrects it. Removed labels (never re-add):
|
||||||
|
- `org.opencontainers.image.base.name` — belongs on the base image, not this image
|
||||||
|
- `org.opencontainers.image.schema-version` — non-spec; redundant with `version`
|
||||||
|
- Any duplicate `authors` or `source` entries
|
||||||
|
|
||||||
|
## HOSTNAME convention
|
||||||
|
|
||||||
|
All templates set `ENV HOSTNAME="casjaysdevdocker-${IMAGE_NAME}"` in every stage that
|
||||||
|
declares it. The prefix is always `casjaysdevdocker-`, never `casjaysdev-`.
|
||||||
|
|
||||||
|
## `GEN_DOCKERFILE_APP_DIR` and pull URL logic
|
||||||
|
|
||||||
|
`GEN_DOCKERFILE_APP_DIR` is auto-detected by `gen-dockerfile` from the parent directory
|
||||||
|
of `$PWD` (the org the checkout lives in):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GEN_DOCKERFILE_APP_DIR="${GEN_DOCKERFILE_APP_DIR:-$(basename -- "$(dirname -- "$PWD")")}"
|
||||||
|
```
|
||||||
|
|
||||||
|
It selects the `GEN_DOCKER_SPECIFY_IMAGE_SOURCE_*` defaults:
|
||||||
|
|
||||||
|
- `casjaysdevdocker/*` repos → `FROM casjaysdev/<distro>:latest` (pre-built, multi-arch)
|
||||||
|
- `dockersrc/*` and all other orgs → `FROM <distro>:latest` (upstream official images)
|
||||||
|
|
||||||
|
App repos must resolve to the `casjaysdev/*` branch — a checkout outside
|
||||||
|
`~/Projects/*/casjaysdevdocker/` needs `GEN_DOCKERFILE_APP_DIR="casjaysdevdocker"`
|
||||||
|
exported before calling `gen-dockerfile`, or the regenerated Dockerfile silently reverts
|
||||||
|
to upstream distro pulls (rule 8 violation).
|
||||||
|
|
||||||
|
## Arch Linux apps
|
||||||
|
|
||||||
|
`casjaysdev/archlinux` is a multi-arch manifest (`linux/amd64` + `linux/arm64`), so app
|
||||||
|
repos use a single `FROM ${PULL_URL}:${DISTRO_VERSION} AS build` — the three-stage
|
||||||
|
`base-${TARGETARCH}` FROM block belongs to the base repo only.
|
||||||
|
|
||||||
|
## `web.template` / `xorg.template` notes
|
||||||
|
|
||||||
|
`web` apps inherit the systemd + noVNC stack (`SERVICE_PORT="5800"`,
|
||||||
|
`EXPOSE_PORTS="5800 5900"` defaults); `xorg` apps inherit the systemd + Xorg stack. App
|
||||||
|
packages go in `ENV_PACKAGES` / `02-packages.sh`, never by editing the template's stack
|
||||||
|
list.
|
||||||
|
|
||||||
|
## `debian.template` / `ubuntu.template` — RUN continuation
|
||||||
|
|
||||||
|
The first `RUN` block must have `; \` after the `echo` line so
|
||||||
|
`export DEBIAN_FRONTEND=noninteractive` executes before `apt-get`:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
RUN set -e; \
|
||||||
|
echo "Updating the system"; \
|
||||||
|
export DEBIAN_FRONTEND=noninteractive; \
|
||||||
|
apt-get update && apt-get upgrade -yy && apt-get dist-upgrade -yy
|
||||||
|
```
|
||||||
|
|
||||||
|
Without the `; \` the export is a no-op and `apt-get` may prompt interactively.
|
||||||
|
|
||||||
|
## Template resolution order
|
||||||
|
|
||||||
|
1. `$GEN_DOCKERFILE_CONFIG_DIR/templates/<name>.template` (user override)
|
||||||
|
2. `/usr/local/share/CasjaysDev/scripts/templates/dockerfiles/<name>.template`
|
||||||
|
(installed; `$CASJAYSDEVDIR/templates/dockerfiles/` in a dev checkout)
|
||||||
|
|
||||||
|
`template_options.source` is sourced after `__set_variables`, allowing template-specific
|
||||||
|
variable overrides.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 3: TOOLING — gen-dockerfile & gen-script
|
||||||
|
|
||||||
|
## `gen-dockerfile`
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage: gen-dockerfile [options] [dir] [template] [repo-name] [git-repo-url]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Flag | Meaning |
|
||||||
|
|------|---------|
|
||||||
|
| `--update` | Rewrite `.env.scripts` (add/drop vars against the current template) and update ARG/LABEL lines in the `Dockerfile`. Touches no other file. |
|
||||||
|
| `--nogit` | Do not init or commit a git repo — required inside an existing repo. |
|
||||||
|
| `--dir PATH` | Operate on / write output to PATH instead of `$PWD`. |
|
||||||
|
| `--template NAME` | Template to use (`alpine`, `debian`, `ubuntu`, `rhel`, `archlinux`, `scratch`, `web`, `xorg`). Defaults to `alpine`. |
|
||||||
|
| `--repo NAME` | Registry repo name (image basename). Defaults to the directory name. |
|
||||||
|
| `--org NAME` | Registry owner / GitHub org (`--user` is an alias). Prefix `git:` or `reg:` to scope to one system; bare value sets both. For app repos both are `casjaysdevdocker`. |
|
||||||
|
| `--registry URL` | Registry provider URL (e.g. `https://docker.io`). |
|
||||||
|
| `--tag VERSION` | Image version tag (default `latest`). |
|
||||||
|
| `--add-tags TAGS` | Comma-separated additional tags (`USE_DATE` = auto date tag). |
|
||||||
|
| `--distro-name IMG` | Base image pull URL (overrides `ENV_PULL_URL`). |
|
||||||
|
| `--distro-version T` | Base image tag (overrides `ENV_DISTRO_TAG`). |
|
||||||
|
| `--startup FILE` | Generate an init.d service script at `rootfs/usr/local/etc/docker/init.d/FILE` via `gen-script other/start-service`. |
|
||||||
|
| `--dockerfile` | Regenerate the Dockerfile only. |
|
||||||
|
| `--force` | Overwrite existing files without prompting. |
|
||||||
|
|
||||||
|
Resolution order when a value is not given by a flag: flags → git remote → project dirs →
|
||||||
|
defaults.
|
||||||
|
|
||||||
|
Special subcommand — `gen-dockerfile actions` writes `.gitea/workflows/build.yml` from
|
||||||
|
the existing `Dockerfile` (PART 7). App repos have no versioned `build.{ver}.yml` files.
|
||||||
|
|
||||||
|
## `gen-script`
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage: gen-script [options] [template] [filename]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Flag / env var | Meaning |
|
||||||
|
|----------------|---------|
|
||||||
|
| `--dir PATH` | Write the generated file to `PATH/filename`. |
|
||||||
|
| `-n` / `--name VALUE` | Service name substituted into the template — fills `REPLACE_SERVICE_NAME` in `other/start-service`, pre-populating `SERVICE_NAME=` without a sed step. |
|
||||||
|
| `GEN_SCRIPT_OVERWRITE="Y"` | Overwrite the output without prompting (default `"A"` = ask). Required when the target exists, even with `GEN_SCRIPT_EDITFILE="N"`. |
|
||||||
|
| `GEN_SCRIPT_EDITFILE="N"` | Suppress the interactive editor after generation. `-e`/`--no` sets BOTH this AND `GEN_SCRIPT_OVERWRITE="Y"`; the env var alone does not. |
|
||||||
|
| `other/start-service` | Template path — positional arg 1, slash-joined words, matching the `@@Template` header. |
|
||||||
|
| `filename` | Output basename — positional arg 2, combined with `--dir`. |
|
||||||
|
|
||||||
|
Other flags: `-k`/`--keep` (never overwrite), `--replace` (new header replaces old),
|
||||||
|
`-d`/`--desc` (header description), `-p`/`--prev` (copy header metadata from a file).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 4: `.env.scripts` REFERENCE
|
||||||
|
|
||||||
|
Generated at the repo root; sourced by `gen-dockerfile` and by CI at build time. App
|
||||||
|
repos carry exactly one. It is a pure `KEY="value"` file — no logic.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Variable | Purpose |
|
||||||
|
|----------|---------|
|
||||||
|
| `ENV_DOCKERFILE` | Dockerfile to build (`Dockerfile`) |
|
||||||
|
| `ENV_REGISTRY_REPO` | Image name in the registry (`{name}`) |
|
||||||
|
| `ENV_REGISTRY_ORG` | Registry namespace — `casjaysdevdocker` for app repos |
|
||||||
|
| `ENV_REGISTRY_URL` | Registry base URL (`https://docker.io`) — pull/push host, never a label URL |
|
||||||
|
| `ENV_REGISTRY_PUSH` | Full push path `org/repo` (`casjaysdevdocker/{name}`) |
|
||||||
|
| `ENV_ADD_IMAGE_PUSH` | Extra push destinations |
|
||||||
|
| `ENV_GIT_REPO_URL` | Full Git repo URL — `https://github.com/casjaysdevdocker/{name}`; feeds the `source`/`documentation` labels, so a wrong value here regresses labels on regeneration |
|
||||||
|
| `ENV_USE_TEMPLATE` | Template name (`alpine`, `debian`, …) — the authoritative record of which base family the app builds on |
|
||||||
|
| `ENV_PULL_URL` | Base image to pull FROM (`casjaysdev/<base>`) |
|
||||||
|
| `ENV_DISTRO_TAG` | Tag for the pull image (`latest`) |
|
||||||
|
| `ENV_IMAGE_TAG` | Default image tag (`latest`) |
|
||||||
|
| `ENV_ADD_TAGS` | Additional comma-separated tags; `USE_DATE` auto-generates a date tag |
|
||||||
|
| `ENV_PACKAGES` | Space-separated package list |
|
||||||
|
| `ENV_VENDOR` / `ENV_AUTHOR` / `ENV_MAINTAINER` | Label metadata |
|
||||||
|
| `SERVICE_PORT` | Primary exposed port — apps normally set this |
|
||||||
|
| `EXPOSE_PORTS` | Additional exposed ports |
|
||||||
|
| `PHP_VERSION` / `NODE_VERSION` / `NODE_MANAGER` | Runtime versions (`system` default) |
|
||||||
|
| `WWW_ROOT_DIR` | Web root (`/usr/local/share/httpd/default`) |
|
||||||
|
| `DOCKER_ENTYPOINT_PORTS_WEB` / `DOCKER_ENTYPOINT_PORTS_SRV` | Ports passed to the entrypoint |
|
||||||
|
| `DOCKER_ENTYPOINT_HEALTH_APPS` / `DOCKER_ENTYPOINT_HEALTH_ENDPOINTS` | Healthcheck targets |
|
||||||
|
|
||||||
|
## Legacy variable auto-migration
|
||||||
|
|
||||||
|
`gen-dockerfile` calls `__migrate_env_script` on every run, renaming old variables:
|
||||||
|
|
||||||
|
| 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 files. Retired variables that must not reappear anywhere:
|
||||||
|
`DEFAULT_TEMPLATE_DIR`, `DEFAULT_FILE_DIR`, `DEFAULT_DATA_DIR`, `DEFAULT_CONF_DIR`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 5: RUNTIME SYSTEM — SETUP SCRIPTS, ENTRYPOINT, INIT.D
|
||||||
|
|
||||||
|
## Build-time setup scripts (`rootfs/root/docker/setup/`)
|
||||||
|
|
||||||
|
Run in order inside the build stage:
|
||||||
|
|
||||||
|
| Script | Role |
|
||||||
|
|--------|------|
|
||||||
|
| `00-init.sh` | Initialize base directory structure and environment |
|
||||||
|
| `01-system.sh` | Repos, locales, timezone, system settings |
|
||||||
|
| `02-packages.sh` | App-specific packages, package managers, language runtimes |
|
||||||
|
| `03-files.sh` | Install staged files (`rootfs/tmp/etc/*` → `/etc/*`), permissions, symlinks |
|
||||||
|
| `04-users.sh` | Create service users/groups |
|
||||||
|
| `05-custom.sh` | Application install logic — the heart of an app repo |
|
||||||
|
| `06-post.sh` | Post-install configuration |
|
||||||
|
| `07-cleanup.sh` | Remove build deps, caches, temp files |
|
||||||
|
|
||||||
|
**`05-custom.sh` ownership:** the upstream template ships an empty stub. An app repo's
|
||||||
|
`05-custom.sh` carries the application install (download/build, users, default config) —
|
||||||
|
that content exists only in the repo's git history, never in the template. On
|
||||||
|
regeneration, keep the existing body and pull forward only boilerplate (version-stamp
|
||||||
|
header, `set` line, shellcheck-disable line). The same rule applies to any other `0*.sh`
|
||||||
|
found to contain real logic beyond the stub.
|
||||||
|
|
||||||
|
## Entrypoint flow
|
||||||
|
|
||||||
|
```
|
||||||
|
tini → /usr/local/bin/entrypoint.sh
|
||||||
|
├─ Load /usr/local/etc/docker/functions/entrypoint.sh
|
||||||
|
├─ Source env: /root/env.sh, /usr/local/etc/docker/env/*.sh, /config/env/*.sh
|
||||||
|
├─ Seed /config and /data on first run
|
||||||
|
├─ __start_init_scripts — source every init.d/*.sh in sort order
|
||||||
|
├─ Handle `healthcheck` command
|
||||||
|
└─ Execute main application
|
||||||
|
```
|
||||||
|
|
||||||
|
`rootfs/usr/local/bin/` generated set: `entrypoint.sh`, `pkmgr`, `symlink`, `copy`,
|
||||||
|
`healthcheck`. `pkmgr` wraps the native package manager (`apk`, `apt-get`, `dnf`,
|
||||||
|
`pacman`) behind `pkmgr update|install|remove|clean`.
|
||||||
|
|
||||||
|
## App-specific bin scripts
|
||||||
|
|
||||||
|
Extra scripts in `rootfs/usr/local/bin/` that `gen-dockerfile` does not generate are
|
||||||
|
repo-owned. Their `@@Template` header governs maintenance:
|
||||||
|
|
||||||
|
- `@@Template : shell/sh` — boilerplate synced from `$TEMPLATE_DIR/scripts/shell/sh`;
|
||||||
|
`#!/usr/bin/env sh`, `set -e` only (`pipefail` is a bashism — must NOT appear)
|
||||||
|
- `@@Template : shell/bash` — synced from `shell/bash`; `set -eo pipefail` required
|
||||||
|
- No `@@Template` header — hand-written; never modified by tooling
|
||||||
|
|
||||||
|
## init.d scripts — critical rules
|
||||||
|
|
||||||
|
**Each service gets its own numbered init.d script. Never merge or remove services.**
|
||||||
|
`__start_init_scripts` sources every `*.sh` in sort order — multi-process apps have one
|
||||||
|
script per daemon (e.g. gitea: `05-dockerd.sh`, `08-gitea.sh`, `zz-act_runner.sh`).
|
||||||
|
|
||||||
|
init.d scripts are **regenerated, never patched in place** — old copies may call functions
|
||||||
|
removed from the current `functions/entrypoint.sh`. Generate fresh via
|
||||||
|
`gen-script other/start-service` (or `gen-dockerfile --startup`), then restore the
|
||||||
|
app-specific values. They are `#!/usr/bin/env bash` with `set -eo pipefail`.
|
||||||
|
|
||||||
|
Required variables in every init.d script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SERVICE_NAME="myapp"
|
||||||
|
EXEC_CMD_BIN='myapp'
|
||||||
|
EXEC_CMD_ARGS=''
|
||||||
|
EXEC_PRE_SCRIPT=''
|
||||||
|
SERVICE_USES_PID=''
|
||||||
|
IS_WEB_SERVER="no"
|
||||||
|
IS_DATABASE_SERVICE="no"
|
||||||
|
USES_DATABASE_SERVICE="no"
|
||||||
|
DATABASE_SERVICE_TYPE="sqlite"
|
||||||
|
RUNAS_USER="root"
|
||||||
|
```
|
||||||
|
|
||||||
|
Directory variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DATA_DIR="/data/$SERVICE_NAME"
|
||||||
|
CONF_DIR="/config/$SERVICE_NAME"
|
||||||
|
ETC_DIR="/etc/$SERVICE_NAME"
|
||||||
|
LOG_DIR="/data/logs/$SERVICE_NAME"
|
||||||
|
TMP_DIR="/tmp/$SERVICE_NAME"
|
||||||
|
RUN_DIR="/run/$SERVICE_NAME"
|
||||||
|
ROOT_FILE_PREFIX="/config/secure/auth/root"
|
||||||
|
USER_FILE_PREFIX="/config/secure/auth/user"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hook functions
|
||||||
|
|
||||||
|
The `start-service` template generates all outer hooks fully implemented — customise via
|
||||||
|
the matching `*_local()` stub, which each outer hook calls automatically if defined:
|
||||||
|
|
||||||
|
| Outer hook (do not redefine) | Customise via |
|
||||||
|
|------------------------------|---------------|
|
||||||
|
| `__run_precopy` | `__run_precopy_local` |
|
||||||
|
| `__execute_prerun` | `__execute_prerun_local` |
|
||||||
|
| `__run_pre_execute_checks` | `__run_pre_execute_checks_local` |
|
||||||
|
| `__update_conf_files` | `__update_conf_files_local` |
|
||||||
|
| `__pre_execute` | `__pre_execute_local` |
|
||||||
|
| `__post_execute` | `__post_execute_local` |
|
||||||
|
| `__pre_message` | `__pre_message_local` |
|
||||||
|
| `__update_ssl_conf` | `__update_ssl_conf_local` |
|
||||||
|
| `__create_service_env` | — |
|
||||||
|
| `__run_start_script` | — |
|
||||||
|
| `__run_secure_function` | — |
|
||||||
|
|
||||||
|
## PID sentinel guard
|
||||||
|
|
||||||
|
Every init.d script must guard on exactly this sentinel — leading dot, no underscores in
|
||||||
|
the filename portion; any other form silently skips the guard:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Volumes
|
||||||
|
|
||||||
|
- `/config` — persistent configuration
|
||||||
|
- `/data` — persistent application data
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 6: README.md STANDARD LAYOUT
|
||||||
|
|
||||||
|
App image layout (`casjaysdevdocker/{name}` → `casjaysdevdocker/{name}`). Substitute
|
||||||
|
`{name}` and `{port}` (the value of `SERVICE_PORT`); omit all `-p`/`ports:` sections only
|
||||||
|
in the rare case `SERVICE_PORT` is empty.
|
||||||
|
|
||||||
|
**Hand-crafted README exception:** a repo whose README deliberately diverges from this
|
||||||
|
layout (full env-var tables, app-specific quick-start flags — e.g. gitea) owns its README.
|
||||||
|
Update its facts (image name, org, ports, URLs), never rewrite its structure back to the
|
||||||
|
generated layout.
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
## 👋 Welcome to {name} 🚀
|
||||||
|
|
||||||
|
{name} README
|
||||||
|
|
||||||
|
|
||||||
|
## Install my system scripts
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo bash -c "$(curl -q -LSsf "https://github.com/systemmgr/installer/raw/main/install.sh")"
|
||||||
|
sudo systemmgr --config && sudo systemmgr install scripts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Automatic install/update
|
||||||
|
|
||||||
|
```shell
|
||||||
|
dockermgr update {name}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install and run container
|
||||||
|
|
||||||
|
```shell
|
||||||
|
dockerHome="/srv/$USER/docker/casjaysdevdocker/{name}/latest/volumes"
|
||||||
|
mkdir -p "$dockerHome"
|
||||||
|
git clone "https://github.com/dockermgr/{name}" "$HOME/.local/share/CasjaysDev/dockermgr/{name}"
|
||||||
|
cp -Rfva "$HOME/.local/share/CasjaysDev/dockermgr/{name}/volumes/." "$dockerHome/"
|
||||||
|
docker run -d \
|
||||||
|
--restart always \
|
||||||
|
--privileged \
|
||||||
|
--name casjaysdevdocker-{name}-latest \
|
||||||
|
--hostname {name} \
|
||||||
|
-e TZ=${TIMEZONE:-America/New_York} \
|
||||||
|
-v "$dockerHome/data:/data:z" \
|
||||||
|
-v "$dockerHome/config:/config:z" \
|
||||||
|
-p {port}:{port} \
|
||||||
|
casjaysdevdocker/{name}:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## via docker-compose
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
opencloud:
|
ProjectName:
|
||||||
image: casjaysdevdocker/opencloud:latest
|
image: casjaysdevdocker/{name}
|
||||||
container_name: opencloud
|
container_name: casjaysdevdocker-{name}
|
||||||
command: start
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
environment:
|
environment:
|
||||||
- ADMIN_PASSWORD=changeme
|
- TZ=America/New_York
|
||||||
- OC_LOG_LEVEL=info
|
- HOSTNAME={name}
|
||||||
volumes:
|
volumes:
|
||||||
- opencloud-config:/config
|
- "/srv/$USER/docker/casjaysdevdocker/{name}/latest/volumes/data:/data:z"
|
||||||
- opencloud-data:/data
|
- "/srv/$USER/docker/casjaysdevdocker/{name}/latest/volumes/config:/config:z"
|
||||||
restart: unless-stopped
|
ports:
|
||||||
|
- {port}:{port}
|
||||||
volumes:
|
restart: always
|
||||||
opencloud-config:
|
|
||||||
opencloud-data:
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Nginx Proxy Configuration
|
## Get source files
|
||||||
|
|
||||||
The built-in Nginx proxies requests to all services:
|
```shell
|
||||||
|
dockermgr download src casjaysdevdocker/{name}
|
||||||
|
```
|
||||||
|
|
||||||
- `/` → OpenCloud (port 9200)
|
OR
|
||||||
- `/cool/` → Collabora WebSocket
|
|
||||||
- `/hosting/discovery` → Collabora discovery
|
|
||||||
- `/hosting/capabilities` → Collabora capabilities
|
|
||||||
- `/radicale/` → Radicale CalDAV/CardDAV
|
|
||||||
|
|
||||||
## Building
|
```shell
|
||||||
|
git clone "https://github.com/casjaysdevdocker/{name}" "$HOME/Projects/github/casjaysdevdocker/{name}"
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
## Build container
|
||||||
# Generate Dockerfile from template
|
|
||||||
gen-dockerfile
|
|
||||||
|
|
||||||
# Build for current platform
|
```shell
|
||||||
|
cd "$HOME/Projects/github/casjaysdevdocker/{name}"
|
||||||
buildx
|
buildx
|
||||||
|
|
||||||
# Build for specific platform
|
|
||||||
buildx --platform linux/amd64
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Technical Notes
|
## Authors
|
||||||
|
|
||||||
### Base Image
|
🤖 casjay: [Github](https://github.com/casjay) 🤖
|
||||||
|
⛵ casjaysdevdocker: [Github](https://github.com/casjaysdevdocker) [Docker](https://hub.docker.com/u/casjaysdevdocker) ⛵
|
||||||
|
````
|
||||||
|
|
||||||
Uses Debian-based image (not Alpine) because Collabora CODE requires glibc.
|
---
|
||||||
|
|
||||||
### SSL/TLS
|
# PART 7: CI/CD WORKFLOWS
|
||||||
|
|
||||||
SSL is disabled by default (`PROXY_TLS=false`) as the container is designed to run behind a reverse proxy that handles TLS termination.
|
## Generated workflow (`gen-dockerfile actions`)
|
||||||
|
|
||||||
### ClamAV Virus Definitions
|
`gen-dockerfile actions` writes `.gitea/workflows/build.yml` from the current
|
||||||
|
`Dockerfile`. App repos get the single `build.yml` only — no versioned variants. All
|
||||||
|
actions are SHA-pinned — never tag-pinned.
|
||||||
|
|
||||||
Virus definitions are downloaded during image build to reduce container startup time. Updates can be performed by restarting the container or running `freshclam` manually.
|
- **Triggers:** `push` to `main`, monthly schedule, `workflow_dispatch`
|
||||||
|
- **Registry strategy:** always logs in to the Gitea registry via the auto-provided
|
||||||
|
`GITEA_TOKEN`; conditionally logs in to Docker Hub when `vars.DOCKER_USERNAME` is set
|
||||||
|
(`vars.DOCKER_USERNAME` + `secrets.DOCKER_PASSWORD`; `vars.DOCKER_REGISTRY` overrides
|
||||||
|
the registry, `vars.DOCKER_ORG` the namespace)
|
||||||
|
- **Platforms:** `linux/amd64,linux/arm64`
|
||||||
|
- **build-args:** only `BUILD_DATE`, `GIT_COMMIT`, `BUILD_VERSION`
|
||||||
|
- **Tags pushed:** date tag (`yymm`) + `latest` to both registries
|
||||||
|
- **Annotations:** mirror the OCI label standard (PART 2), with `url`/`source`/
|
||||||
|
`documentation` set to the workflow's repository URL
|
||||||
|
|
||||||
### Collabora User
|
## Legacy workflow (`docker.yaml`)
|
||||||
|
|
||||||
Collabora CODE must run as the `cool` user (not root) for security reasons.
|
A hand-crafted `.gitea/workflows/docker.yaml` may exist in older repos — reference copy in
|
||||||
|
the org-level `.github` repo. **Never overwrite it, and never use it as a template for new
|
||||||
|
work** — it uses tag-pinned actions and retired secret names. All new/updated workflows
|
||||||
|
come from `gen-dockerfile actions`.
|
||||||
|
|
||||||
### Admin Password
|
---
|
||||||
|
|
||||||
If `ADMIN_PASSWORD` is not set, a random 16-character password is generated and saved to `/config/secure/auth/root/opencloud_admin_pass`.
|
# PART 8: VERIFICATION & COMMIT
|
||||||
|
|
||||||
## Troubleshooting
|
## Syntax gates
|
||||||
|
|
||||||
### Check Service Status
|
Every touched script must pass before commit:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker logs opencloud-test 2>&1 | grep -E "completed|Error|Starting"
|
for f in rootfs/usr/local/bin/*; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
case "$(head -1 "$f")" in
|
||||||
|
*bash*) bash -n "$f" || exit 1 ;;
|
||||||
|
*sh*) sh -n "$f" || exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
bash -n rootfs/usr/local/etc/docker/functions/entrypoint.sh
|
||||||
|
|
||||||
|
for f in rootfs/root/docker/setup/0*.sh rootfs/usr/local/etc/docker/init.d/*.sh; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
bash -n "$f" || exit 1
|
||||||
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
### Access Container Shell
|
## Dead-reference gates
|
||||||
|
|
||||||
|
After any regeneration:
|
||||||
|
|
||||||
|
1. No script references an env var removed from `.env.scripts` (diff-driven check).
|
||||||
|
2. No script calls a function absent from both the current
|
||||||
|
`functions/entrypoint.sh` and the script itself.
|
||||||
|
3. No `__copy_templates` calls remain (retired with `DEFAULT_TEMPLATE_DIR`).
|
||||||
|
4. `Dockerfile` still pulls `FROM casjaysdev/*` (rule 8) — an upstream distro pull means
|
||||||
|
`GEN_DOCKERFILE_APP_DIR` resolved wrong during regeneration.
|
||||||
|
|
||||||
|
## Commit
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker exec -it opencloud /bin/bash
|
git status --porcelain
|
||||||
|
git diff --stat
|
||||||
```
|
```
|
||||||
|
|
||||||
### Check Running Processes
|
Write `.git/COMMIT_MESS` from the actual diff — subject ≤64 chars, body as
|
||||||
|
`- path: change` bullets covering every changed file. Then:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker exec opencloud ps aux
|
gitcommit --dir "$(git rev-parse --show-toplevel)" all
|
||||||
```
|
```
|
||||||
|
|
||||||
### View Service Logs
|
`git commit` / `git push` directly are forbidden. Never commit with a failing syntax
|
||||||
|
gate.
|
||||||
```bash
|
|
||||||
# OpenCloud
|
|
||||||
docker exec opencloud cat /data/logs/opencloud/opencloud.log
|
|
||||||
|
|
||||||
# Nginx
|
|
||||||
docker exec opencloud cat /data/logs/nginx/access.log
|
|
||||||
|
|
||||||
# Collabora
|
|
||||||
docker exec opencloud cat /data/logs/collabora/collabora.log
|
|
||||||
```
|
|
||||||
|
|
||||||
## Files Structure
|
|
||||||
|
|
||||||
### Setup Scripts (`/root/docker/setup/`)
|
|
||||||
|
|
||||||
- `02-packages.sh` - Base package installation info (actual packages in ENV_PACKAGES)
|
|
||||||
- `03-files.sh` - Downloads OpenCloud binary, Tika JAR, ClamAV definitions
|
|
||||||
- `04-users.sh` - Creates opencloud user and group
|
|
||||||
|
|
||||||
### Init Scripts (`/usr/local/etc/docker/init.d/`)
|
|
||||||
|
|
||||||
- `01-clamav.sh` - ClamAV antivirus daemon
|
|
||||||
- `02-tika.sh` - Apache Tika search service
|
|
||||||
- `03-radicale.sh` - Radicale CalDAV/CardDAV
|
|
||||||
- `04-collabora.sh` - Collabora CODE
|
|
||||||
- `05-opencloud.sh` - OpenCloud server
|
|
||||||
- `06-nginx.sh` - Nginx reverse proxy
|
|
||||||
|
|
||||||
### Configuration Templates
|
|
||||||
|
|
||||||
- `/usr/local/share/template-files/config/` - Default configurations
|
|
||||||
- `/etc/radicale/config` - Radicale default config
|
|
||||||
|
|
||||||
## Package List
|
|
||||||
|
|
||||||
Packages installed via `ENV_PACKAGES`:
|
|
||||||
|
|
||||||
- Core: ca-certificates, curl, wget, bash, tini, gnupg, apt-transport-https, tzdata, procps, netcat-openbsd, nginx
|
|
||||||
- Java: default-jre-headless (for Tika)
|
|
||||||
- Python: python3, python3-pip, python3-venv (for Radicale)
|
|
||||||
- Antivirus: clamav, clamav-daemon, clamav-freshclam
|
|
||||||
|
|
||||||
Additional packages installed in `03-files.sh`:
|
|
||||||
- Collabora: coolwsd, code-brand (from Collabora repository)
|
|
||||||
- Radicale: installed via pip
|
|
||||||
|
|
||||||
## Version Information
|
|
||||||
|
|
||||||
- OpenCloud: Latest from GitHub releases (auto-detected)
|
|
||||||
- Apache Tika: 2.9.1
|
|
||||||
- Radicale: Latest from pip
|
|
||||||
- Collabora CODE: Latest from official repository
|
|
||||||
|
|
||||||
## Known Limitations
|
|
||||||
|
|
||||||
1. Single container deployment - not suitable for high-availability setups
|
|
||||||
2. ClamAV definitions are from build time - consider periodic updates
|
|
||||||
3. No built-in SSL - requires external reverse proxy for HTTPS
|
|
||||||
4. Memory intensive due to Java (Tika) and Collabora
|
|
||||||
5. Collabora CODE may require additional configuration for production use - check `/usr/bin/discovery.xml`
|
|
||||||
|
|
||||||
## Current Service Status
|
|
||||||
|
|
||||||
- **ClamAV**: Working
|
|
||||||
- **Apache Tika**: Working
|
|
||||||
- **Radicale**: Working
|
|
||||||
- **Collabora CODE**: Requires additional configuration (may fail on first run)
|
|
||||||
- **OpenCloud**: Dependent on Collabora for document editing features
|
|
||||||
- **Nginx**: Working (serves as reverse proxy)
|
|
||||||
|
|
||||||
## Related Links
|
|
||||||
|
|
||||||
- [OpenCloud GitHub](https://github.com/opencloud-eu/opencloud)
|
|
||||||
- [Collabora CODE](https://www.collaboraoffice.com/code/)
|
|
||||||
- [Apache Tika](https://tika.apache.org/)
|
|
||||||
- [Radicale](https://radicale.org/)
|
|
||||||
- [ClamAV](https://www.clamav.net/)
|
|
||||||
|
|||||||
@@ -0,0 +1,310 @@
|
|||||||
|
# OpenCloud All-in-One Docker Container
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This is a comprehensive all-in-one Docker container for OpenCloud, a Go-based file sync and share platform (successor to ownCloud). The container includes all integrated services required for a full-featured deployment.
|
||||||
|
|
||||||
|
## Included Services
|
||||||
|
|
||||||
|
| Service | Port | Purpose |
|
||||||
|
|---------|------|---------|
|
||||||
|
| ClamAV | Socket | Antivirus scanning for uploaded files |
|
||||||
|
| Apache Tika | 9998 | Full-text search and document extraction |
|
||||||
|
| Radicale | 5232 | CalDAV/CardDAV for Calendar and Contacts |
|
||||||
|
| Collabora CODE | 9980 | Document collaboration (LibreOffice Online) |
|
||||||
|
| OpenCloud | 9200 | Core file sync/share platform |
|
||||||
|
| Nginx | 80 | Reverse proxy (external-facing) |
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
+------------------+
|
||||||
|
| Nginx |
|
||||||
|
| (Port 80) |
|
||||||
|
+--------+---------+
|
||||||
|
|
|
||||||
|
+--------------------+--------------------+
|
||||||
|
| | | | |
|
||||||
|
+----v----+ +---v---+ +---v---+ +---v---+ +---v---+
|
||||||
|
|OpenCloud| |Collabora| |Radicale| | Tika | |ClamAV |
|
||||||
|
| (9200) | | (9980) | | (5232) | |(9998)| |(sock) |
|
||||||
|
+---------+ +---------+ +--------+ +------+ +-------+
|
||||||
|
```
|
||||||
|
|
||||||
|
## Service Startup Order
|
||||||
|
|
||||||
|
Services start in this order to ensure dependencies are available:
|
||||||
|
|
||||||
|
1. **01-clamav.sh** - Antivirus daemon (other services depend on scanning)
|
||||||
|
2. **02-tika.sh** - Search indexing service
|
||||||
|
3. **03-radicale.sh** - Calendar/Contacts (CalDAV/CardDAV)
|
||||||
|
4. **04-collabora.sh** - Document collaboration
|
||||||
|
5. **05-opencloud.sh** - Main OpenCloud server
|
||||||
|
6. **06-nginx.sh** - Reverse proxy (last, needs all backends)
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
### Domain Configuration
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `DOMAIN` | `$HOSTNAME` | Primary domain for all services |
|
||||||
|
| `OC_DOMAIN` | `${DOMAIN:-$HOSTNAME}` | OpenCloud domain (for URL generation) |
|
||||||
|
| `COLLABORA_SERVER_NAME` | `collabora.${DOMAIN:-$HOSTNAME}` | Collabora server identifier for WOPI |
|
||||||
|
|
||||||
|
### URL Structure
|
||||||
|
|
||||||
|
Services are accessed via subdomains:
|
||||||
|
- `${DOMAIN}` → OpenCloud (main application)
|
||||||
|
- `collabora.${DOMAIN}` → Collabora CODE (document editing)
|
||||||
|
- `radicale.${DOMAIN}` → Radicale (CalDAV/CardDAV)
|
||||||
|
|
||||||
|
### OpenCloud Configuration
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `ADMIN_PASSWORD` | (auto-generated) | Admin user password |
|
||||||
|
| `OC_CONFIG_DIR` | `/config/opencloud` | Configuration directory |
|
||||||
|
| `OC_DATA_DIR` | `/data/opencloud` | Data storage directory |
|
||||||
|
| `OC_LOG_LEVEL` | `info` | Logging verbosity |
|
||||||
|
| `OC_INSECURE` | `true` | Allow insecure connections |
|
||||||
|
| `PROXY_HTTP_ADDR` | `0.0.0.0:9200` | HTTP listen address |
|
||||||
|
| `PROXY_TLS` | `false` | Disable TLS (behind reverse proxy) |
|
||||||
|
|
||||||
|
### Service Integration
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `COLLABORATION_WOPI_SRC` | `http://localhost:9980` | Collabora WOPI endpoint |
|
||||||
|
| `COLLABORATION_APP_ADDR` | `http://localhost:9980` | Collabora app address |
|
||||||
|
| `SEARCH_EXTRACTOR_TYPE` | `tika` | Search extraction engine |
|
||||||
|
| `SEARCH_EXTRACTOR_TIKA_TIKA_URL` | `http://localhost:9998` | Tika server URL |
|
||||||
|
| `ANTIVIRUS_SCANNER_TYPE` | `clamav` | Antivirus scanner type |
|
||||||
|
| `ANTIVIRUS_CLAMAV_SOCKET` | `/run/clamav/clamd.sock` | ClamAV socket path |
|
||||||
|
|
||||||
|
### User Configuration
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `OPENCLOUD_USER` | `opencloud` | System user for OpenCloud |
|
||||||
|
| `OPENCLOUD_UID` | `1000` | User ID |
|
||||||
|
| `OPENCLOUD_GID` | `1000` | Group ID |
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/config/
|
||||||
|
├── opencloud/ # OpenCloud configuration
|
||||||
|
├── radicale/ # Radicale CalDAV/CardDAV config
|
||||||
|
├── clamav/ # ClamAV configuration
|
||||||
|
├── collabora/ # Collabora configuration
|
||||||
|
└── nginx/ # Nginx reverse proxy config
|
||||||
|
|
||||||
|
/data/
|
||||||
|
├── opencloud/ # User files and data
|
||||||
|
├── radicale/ # Calendar/Contact collections
|
||||||
|
├── clamav/ # ClamAV data
|
||||||
|
├── collabora/ # Collabora data
|
||||||
|
└── logs/ # All service logs
|
||||||
|
├── opencloud/
|
||||||
|
├── radicale/
|
||||||
|
├── clamav/
|
||||||
|
├── tika/
|
||||||
|
├── collabora/
|
||||||
|
└── nginx/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name opencloud \
|
||||||
|
-p 80:80 \
|
||||||
|
-v opencloud-config:/config \
|
||||||
|
-v opencloud-data:/data \
|
||||||
|
casjaysdevdocker/opencloud:latest start
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Custom Admin Password
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name opencloud \
|
||||||
|
-p 80:80 \
|
||||||
|
-e ADMIN_PASSWORD=mysecurepassword \
|
||||||
|
-v opencloud-config:/config \
|
||||||
|
-v opencloud-data:/data \
|
||||||
|
casjaysdevdocker/opencloud:latest start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
opencloud:
|
||||||
|
image: casjaysdevdocker/opencloud:latest
|
||||||
|
container_name: opencloud
|
||||||
|
command: start
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
environment:
|
||||||
|
- ADMIN_PASSWORD=changeme
|
||||||
|
- OC_LOG_LEVEL=info
|
||||||
|
volumes:
|
||||||
|
- opencloud-config:/config
|
||||||
|
- opencloud-data:/data
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
opencloud-config:
|
||||||
|
opencloud-data:
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nginx Proxy Configuration
|
||||||
|
|
||||||
|
The built-in Nginx proxies requests to all services:
|
||||||
|
|
||||||
|
- `/` → OpenCloud (port 9200)
|
||||||
|
- `/cool/` → Collabora WebSocket
|
||||||
|
- `/hosting/discovery` → Collabora discovery
|
||||||
|
- `/hosting/capabilities` → Collabora capabilities
|
||||||
|
- `/radicale/` → Radicale CalDAV/CardDAV
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate Dockerfile from template
|
||||||
|
gen-dockerfile
|
||||||
|
|
||||||
|
# Build for current platform
|
||||||
|
buildx
|
||||||
|
|
||||||
|
# Build for specific platform
|
||||||
|
buildx --platform linux/amd64
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Notes
|
||||||
|
|
||||||
|
### Base Image
|
||||||
|
|
||||||
|
Uses Debian-based image (not Alpine) because Collabora CODE requires glibc.
|
||||||
|
|
||||||
|
### SSL/TLS
|
||||||
|
|
||||||
|
SSL is disabled by default (`PROXY_TLS=false`) as the container is designed to run behind a reverse proxy that handles TLS termination.
|
||||||
|
|
||||||
|
### ClamAV Virus Definitions
|
||||||
|
|
||||||
|
Virus definitions are downloaded during image build to reduce container startup time. Updates can be performed by restarting the container or running `freshclam` manually.
|
||||||
|
|
||||||
|
### Collabora User
|
||||||
|
|
||||||
|
Collabora CODE must run as the `cool` user (not root) for security reasons.
|
||||||
|
|
||||||
|
### Admin Password
|
||||||
|
|
||||||
|
If `ADMIN_PASSWORD` is not set, a random 16-character password is generated and saved to `/config/secure/auth/root/opencloud_admin_pass`.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Check Service Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs opencloud-test 2>&1 | grep -E "completed|Error|Starting"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Access Container Shell
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it opencloud /bin/bash
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Running Processes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec opencloud ps aux
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Service Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# OpenCloud
|
||||||
|
docker exec opencloud cat /data/logs/opencloud/opencloud.log
|
||||||
|
|
||||||
|
# Nginx
|
||||||
|
docker exec opencloud cat /data/logs/nginx/access.log
|
||||||
|
|
||||||
|
# Collabora
|
||||||
|
docker exec opencloud cat /data/logs/collabora/collabora.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Files Structure
|
||||||
|
|
||||||
|
### Setup Scripts (`/root/docker/setup/`)
|
||||||
|
|
||||||
|
- `02-packages.sh` - Base package installation info (actual packages in ENV_PACKAGES)
|
||||||
|
- `03-files.sh` - Downloads OpenCloud binary, Tika JAR, ClamAV definitions
|
||||||
|
- `04-users.sh` - Creates opencloud user and group
|
||||||
|
|
||||||
|
### Init Scripts (`/usr/local/etc/docker/init.d/`)
|
||||||
|
|
||||||
|
- `01-clamav.sh` - ClamAV antivirus daemon
|
||||||
|
- `02-tika.sh` - Apache Tika search service
|
||||||
|
- `03-radicale.sh` - Radicale CalDAV/CardDAV
|
||||||
|
- `04-collabora.sh` - Collabora CODE
|
||||||
|
- `05-opencloud.sh` - OpenCloud server
|
||||||
|
- `06-nginx.sh` - Nginx reverse proxy
|
||||||
|
|
||||||
|
### Configuration Templates
|
||||||
|
|
||||||
|
- `/usr/local/share/template-files/config/` - Default configurations
|
||||||
|
- `/etc/radicale/config` - Radicale default config
|
||||||
|
|
||||||
|
## Package List
|
||||||
|
|
||||||
|
Packages installed via `ENV_PACKAGES`:
|
||||||
|
|
||||||
|
- Core: ca-certificates, curl, wget, bash, tini, gnupg, apt-transport-https, tzdata, procps, netcat-openbsd, nginx
|
||||||
|
- Java: default-jre-headless (for Tika)
|
||||||
|
- Python: python3, python3-pip, python3-venv (for Radicale)
|
||||||
|
- Antivirus: clamav, clamav-daemon, clamav-freshclam
|
||||||
|
|
||||||
|
Additional packages installed in `03-files.sh`:
|
||||||
|
- Collabora: coolwsd, code-brand (from Collabora repository)
|
||||||
|
- Radicale: installed via pip
|
||||||
|
|
||||||
|
## Version Information
|
||||||
|
|
||||||
|
- OpenCloud: Latest from GitHub releases (auto-detected)
|
||||||
|
- Apache Tika: 2.9.1
|
||||||
|
- Radicale: Latest from pip
|
||||||
|
- Collabora CODE: Latest from official repository
|
||||||
|
|
||||||
|
## Known Limitations
|
||||||
|
|
||||||
|
1. Single container deployment - not suitable for high-availability setups
|
||||||
|
2. ClamAV definitions are from build time - consider periodic updates
|
||||||
|
3. No built-in SSL - requires external reverse proxy for HTTPS
|
||||||
|
4. Memory intensive due to Java (Tika) and Collabora
|
||||||
|
5. Collabora CODE may require additional configuration for production use - check `/usr/bin/discovery.xml`
|
||||||
|
|
||||||
|
## Current Service Status
|
||||||
|
|
||||||
|
- **ClamAV**: Working
|
||||||
|
- **Apache Tika**: Working
|
||||||
|
- **Radicale**: Working
|
||||||
|
- **Collabora CODE**: Requires additional configuration (may fail on first run)
|
||||||
|
- **OpenCloud**: Dependent on Collabora for document editing features
|
||||||
|
- **Nginx**: Working (serves as reverse proxy)
|
||||||
|
|
||||||
|
## Related Links
|
||||||
|
|
||||||
|
- [OpenCloud GitHub](https://github.com/opencloud-eu/opencloud)
|
||||||
|
- [Collabora CODE](https://www.collaboraoffice.com/code/)
|
||||||
|
- [Apache Tika](https://tika.apache.org/)
|
||||||
|
- [Radicale](https://radicale.org/)
|
||||||
|
- [ClamAV](https://www.clamav.net/)
|
||||||
Reference in New Issue
Block a user