Compare commits

...
5 Commits
Author SHA1 Message Date
tiennm99 7a0a5384ee feat(code-server): add command-line utilities to the image
Install network, DNS, search, lint, database-client and maintenance tools
from Debian so every fresh deploy has them without a setup step.
2026-10-12 20:00:32 +07:00
tiennm99 5b42dc2505 chore: remove code-server-lsio, collabora, onlyoffice and seafile
Drop the four service directories and their rows from the root index, and
replace code-server-lsio in the naming, workspace and environment rules with
a generic example.
2026-10-12 11:28:51 +07:00
tiennm99 fedff373fc docs: keep composes to common third-party services 2026-10-11 11:08:34 +07:00
tiennm99 b3861f4c2f feat(gitea): move the database to PostgreSQL 18 2026-10-11 09:26:34 +07:00
tiennm99 8b33bd9135 feat(owncloud): upgrade to ownCloud 11 with MariaDB 10.11 and Redis 7 2026-10-11 09:20:28 +07:00
24 changed files with 83 additions and 587 deletions

No files matched your search

+1 -1
View File
@@ -52,7 +52,7 @@ Compose interpolation reads the deploying shell's environment before the
`.env` file, so a variable must not share a name with anything the shell
already exports. `HOSTNAME` is the trap: it is set inside every container,
including the one Coolify itself runs in, and would silently win. Hence
`SERVICE_HOSTNAME` in `code-server`, `code-server-lsio` and `paseo`.
`SERVICE_HOSTNAME` in `code-server` and `paseo`.
A value stored in the Coolify app's environment, including its preview copy,
beats the `${VAR:-default}` default in `compose.yml`. Changing a default in the
+7 -7
View File
@@ -9,7 +9,7 @@ shorter one. Not `docker-compose.yml`.
A service directory is named after the software it runs. When two services
package the same software, the one that is not upstream's own image takes a
suffix naming its source — `code-server-lsio` for LinuxServer's code-server —
suffix naming its source — `<software>-lsio` for a LinuxServer image —
so both can coexist. The suffix is a directory name only, forced by the
conflict; it is not a name to copy anywhere else.
@@ -19,12 +19,12 @@ is a starting point, not a spec: adapt it to the conventions in these rules
rather than copying its layout and names as they are.
Inside `compose.yml`, the main service is named after its image —
`code-server`, not `code-server-lsio`. The exception is an image whose name
is not a good identifier on its own — too generic, or longer than it needs to
be. Use a short name that still says which software it is: `owncloud` for
`owncloud/server`, since `server` alone could be anything; `traffmonetizer`
for `traffmonetizer/cli_v2`; `hermes` for `nousresearch/hermes-agent`. A
supporting container is named for its
`code-server` for any code-server image, never a suffixed directory name. The
exception is an image whose name is not a good identifier on its own — too
generic, or longer than it needs to be. Use a short name that still says
which software it is: `owncloud` for `owncloud/server`, since `server` alone
could be anything; `traffmonetizer` for `traffmonetizer/cli_v2`; `hermes`
for `nousresearch/hermes-agent`. A supporting container is named for its
role, so the software behind it can be swapped without renaming: `db` for any
database, `cache` for Redis, Valkey or Memcached, and a short role name such as
`dockerproxy` for anything else (see the examples below). Swapping Redis for
+2 -2
View File
@@ -6,7 +6,7 @@ directory, one mounted at `/workspace`. The home volume holds settings,
credentials and CLI logins; `/workspace` holds the code. Point whatever
variable selects the working directory at `/workspace`.
`code-server`, `code-server-lsio`, `paseo` and `webtop` all follow this. A service with no
`code-server`, `paseo` and `webtop` all follow this. A service with no
human inside it does not — an agent such as `hermes` or `openclaw` keeps the
volume layout of its official Docker guide.
@@ -16,5 +16,5 @@ your extensions and logins.
Check who owns `/workspace` on a fresh volume. Docker creates it `root:root`
unless the image ships the directory, and an image that drops to a non-root
user will not be able to write there. `code-server`, `code-server-lsio` and `webtop` need an
user will not be able to write there. `code-server` and `webtop` need an
explicit `chown` for this reason; `paseo` does not.
+3
View File
@@ -4,6 +4,9 @@ Personal docker compose collection. One directory per service, each holding
`compose.yml`, its own `README.md`, a committed `.env.example`, and a
gitignored `.env`.
Only common, well-known third-party services belong here. Our own tools
ship their compose file in their own repo and deploy from there.
Topic rules live in `.claude/rules/` and load with this file:
- `naming.md` — file, directory, service, volume and network names, with
-4
View File
@@ -78,8 +78,6 @@ Each links to its own README for variables, ports, and storage.
| --- | --- |
| [alloy](alloy/README.md) | Grafana Alloy shipping host and Docker telemetry to Grafana Cloud |
| [code-server](code-server/README.md) | VS Code in the browser from the official image |
| [code-server-lsio](code-server-lsio/README.md) | VS Code in the browser from the LinuxServer image, as a remote dev box |
| [collabora](collabora/README.md) | Collabora Online CODE, an online office suite for WOPI hosts |
| [couchbase](couchbase/README.md) | Couchbase Server |
| [diun](diun/README.md) | Image-update notifier, reading the Docker API through a read-only proxy |
| [gitea](gitea/README.md) | Gitea backed by PostgreSQL |
@@ -88,13 +86,11 @@ Each links to its own README for variables, ports, and storage.
| [hermes](hermes/README.md) | Hermes Agent with its built-in web dashboard |
| [litellm](litellm/README.md) | LiteLLM proxy in front of many LLM providers, with PostgreSQL and Redis |
| [nextcloud](nextcloud/README.md) | Nextcloud file sync and share, with PostgreSQL, Redis and a cron container |
| [onlyoffice](onlyoffice/README.md) | ONLYOFFICE Docs Community Edition, an online document editor |
| [open-webui](open-webui/README.md) | Open WebUI chat interface for OpenAI-compatible and Ollama providers |
| [openclaw](openclaw/README.md) | OpenClaw AI agent gateway, with built-in browser automation |
| [opencloud](opencloud/README.md) | OpenCloud file sync and share, single container with built-in identity provider |
| [owncloud](owncloud/README.md) | ownCloud file sync and share, with MariaDB and Redis |
| [paseo](paseo/README.md) | Paseo coding-agent daemon and web UI |
| [seafile](seafile/README.md) | Seafile CE file sync and share, with MariaDB, Redis and the notification server |
| [traffmonetizer](traffmonetizer/README.md) | TraffMonetizer bandwidth-sharing client |
| [webtop](webtop/README.md) | Ubuntu XFCE desktop in the browser |
-18
View File
@@ -1,18 +0,0 @@
# Copy to .env and fill in. Never commit .env.
#
# cp .env.example .env
# Container hostname, also passed in as HOST -- the name zsh's prompt shows.
SERVICE_HOSTNAME=code-server-lsio
# Web UI login password. Required.
# Also used for SUDO_PASSWORD inside the container.
# Generate one with: openssl rand -base64 24
PASSWORD=
# Git identity baked into the container (author + committer).
GIT_NAME=
GIT_EMAIL=
# Name of the installed web app.
# PWA_APPNAME=code-server
-4
View File
@@ -1,4 +0,0 @@
FROM lscr.io/linuxserver/code-server:latest
# Workspace directory, owned by the container user.
RUN mkdir -p /workspace && chown 1000:1000 /workspace
-87
View File
@@ -1,87 +0,0 @@
# code-server-lsio
[VS Code in the browser](https://github.com/linuxserver/docker-code-server),
from the LinuxServer image, set up as a full remote dev box.
Comes with Go, Node.js 24, Python 3 and zsh via LinuxServer mods, the
`code-server-npmglobal` mod so `npm install -g` lands under `/config` and persists,
plus `bubblewrap`, `gh`, `git`, `glab`, `unzip` and `zip` through
`INSTALL_PACKAGES`. Git author/committer identity is injected from `.env`.
## Docker access
The `universal-docker` mod installs the Docker CLI but no daemon, so the host
socket is bind-mounted at `/var/run/docker.sock` to give it something to talk
to. Containers started from inside are siblings on the host, not children --
bind mounts in them resolve against host paths, so a path under `/config` will
not exist unless the same path exists on the host.
The socket is owned by the host's `docker` group. At startup the mod reads the
socket's GID, creates a group with it if none exists and adds `abc` to it, so
`docker` works without `sudo`. Handing a container the
socket is equivalent to giving it root on the host — that is accepted here
because this is a single-user dev box.
The mount carries `:ro`, which is not a security boundary: it only marks the
socket file read-only, while the Docker API is reached by connecting to the
socket, which a read-only mount does not stop. Full API access, and with it
root on the host, remains. Restricting that would need a socket proxy or a
separate rootless daemon.
## Environment
| Variable | Purpose |
| --- | --- |
| `SERVICE_HOSTNAME` | Container hostname, and the name the shell prompt shows. |
| `PASSWORD` | Web UI login, also the in-container sudo password. Required. |
| `GIT_NAME` / `GIT_EMAIL` | Git author and committer identity |
| `PWA_APPNAME` | Optional. Name of the installed web app; defaults to `code-server`. |
Generate a password with `openssl rand -base64 24`.
The compose file refuses to start when `PASSWORD` is unset or blank. The image
itself would start anyway and serve code-server with no authentication, on a
container that holds the Docker socket.
`SERVICE_HOSTNAME` is used twice: as the container's `hostname:` and as the
`HOST` variable inside it. Coolify injects `HOST=0.0.0.0` into every compose
app, and zsh seeds `$HOST` and the `%m`/`%M` prompt escapes from that variable
rather than calling `gethostname()` — so the prompt reads `0`, the first
dot-separated field of `0.0.0.0`. code-server itself never reads `HOST` — it
binds `[::]:8443` — so overriding it only affects the prompt. bash is
unaffected; its `\h` uses the real hostname.
`PUID`/`PGID` are pinned to `1000` in `compose.yml`; the `Dockerfile` depends
on that (see [Storage](#storage)).
## Networking
Listens on `8443`; point the domain at it.
## Storage
| Volume | Mount | Holds |
| --- | --- | --- |
| `code-server-config` | `/config` | Home directory: settings, extensions, shell history, CLI logins |
| `code-server-workspace` | `/workspace` | Code you work on |
`DEFAULT_WORKSPACE` points at `/workspace`. It only chooses the folder
code-server opens; it does not move anything.
The `Dockerfile` exists only to make `/workspace` writable. The image
hard-codes what it hands to the `abc` user — `init-adduser` takes `/app`,
`/config` and `/defaults`, `init-code-server` takes `/config/workspace` by
literal path — and reads `DEFAULT_WORKSPACE` only to decide which folder to
open. A named volume on `/workspace` is therefore never chowned, comes up
`root:root`, and the editor cannot write a single file into it.
Creating the directory in the image, owned by `1000:1000`, fixes it without any
runtime step: Docker seeds an empty named volume from the image directory,
ownership included, so `/workspace` arrives owned by `abc`.
The alternative was a `chown` script in `/custom-cont-init.d`, the image's own
init hook. It was rejected because it needs a bind mount from the repository
into the container, and because the hook silently skips any script that has
lost its executable bit — a read-only workspace with nothing obvious to blame.
Baking `1000:1000` into the image costs the ability to change `PUID` at
runtime, which is free here because `compose.yml` pins it.
-28
View File
@@ -1,28 +0,0 @@
services:
code-server:
build: .
restart: unless-stopped
hostname: ${SERVICE_HOSTNAME}
environment:
- PUID=1000
- PGID=1000
- PASSWORD=${PASSWORD:?required}
- SUDO_PASSWORD=${PASSWORD}
- 'DOCKER_MODS=linuxserver/mods:universal-package-install|linuxserver/mods:universal-docker|linuxserver/mods:code-server-golang|linuxserver/mods:code-server-nodejs|linuxserver/mods:code-server-npmglobal|linuxserver/mods:code-server-python3|linuxserver/mods:code-server-zsh'
- INSTALL_PACKAGES=bubblewrap|gh|git|glab|unzip|zip
- NODEJS_MOD_VERSION=24
- TZ=Asia/Ho_Chi_Minh
- DEFAULT_WORKSPACE=/workspace
- GIT_AUTHOR_NAME=${GIT_NAME}
- GIT_AUTHOR_EMAIL=${GIT_EMAIL}
- GIT_COMMITTER_NAME=${GIT_NAME}
- GIT_COMMITTER_EMAIL=${GIT_EMAIL}
- HOST=${SERVICE_HOSTNAME}
# - PWA_APPNAME=${PWA_APPNAME:-code-server}
volumes:
- 'code-server-config:/config'
- 'code-server-workspace:/workspace'
- '/var/run/docker.sock:/var/run/docker.sock:ro'
volumes:
code-server-config:
code-server-workspace:
+11
View File
@@ -8,6 +8,17 @@ RUN apt-get update \
build-essential bubblewrap unzip zip \
&& rm -rf /var/lib/apt/lists/*
# Command-line utilities: network and DNS, search and review, lint,
# database clients, and backup and maintenance.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
iproute2 lsof bind9-dnsutils netcat-openbsd socat mtr-tiny whois \
tree fd-find fzf git-delta bat \
shellcheck shfmt yamllint \
postgresql-client sqlite3 redis-tools \
ncdu tmux restic rclone age hyperfine entr 7zip \
&& rm -rf /var/lib/apt/lists/*
# Workspace directory, owned by the container user.
RUN mkdir -p /workspace && chown 1000:1000 /workspace
+18 -2
View File
@@ -4,8 +4,8 @@
official `codercom/code-server` image.
The image ships code-server on Debian with `git`, `zsh`, `curl`, `sudo` and a
few editors. The `Dockerfile` adds `build-essential`, `bubblewrap`, `zip` and
`unzip`, and wraps the entrypoint so it starts in `/workspace` and `coder` can
few editors. The `Dockerfile` adds `build-essential`, `bubblewrap`, `zip`,
`unzip` and a set of command-line utilities, and wraps the entrypoint so it starts in `/workspace` and `coder` can
use the Docker socket. Language toolchains and CLIs are installed into home,
below.
@@ -231,6 +231,22 @@ image ships none of them. `build-essential` supplies the `gcc` and `make` that
native builds expect: the linker `cargo` calls, node-gyp addons and Python
sdists.
The command-line utilities are Debian packages too, kept in the image as a
baseline every fresh deploy has without a setup step:
| Group | Packages |
| --- | --- |
| Network and DNS | `iproute2` (`ss`, `ip`), `lsof`, `bind9-dnsutils` (`dig`), `netcat-openbsd` (`nc`), `socat`, `mtr-tiny`, `whois` |
| Search and review | `tree`, `fd-find`, `fzf`, `git-delta`, `bat` |
| Lint | `shellcheck`, `shfmt`, `yamllint` |
| Database clients | `postgresql-client` (`psql`), `sqlite3`, `redis-tools` (`redis-cli`) |
| Maintenance | `ncdu`, `tmux`, `restic`, `rclone`, `age`, `hyperfine`, `entr`, `7zip` (`7z`) |
Debian installs `fd` as `fdfind` and `bat` as `batcat`. They track Debian 13's
versions, which lag upstream: `rclone` is 1.60, and `postgresql-client` is 17,
whose `pg_dump` refuses to dump a newer server. Install a newer copy into home
when one of these falls short.
Everything else lives in home rather than the image, so the image build stays
small and a redeploy does not reinstall or upgrade tools behind your back;
upgrading is the same commands run again.
-18
View File
@@ -1,18 +0,0 @@
# Copy to .env and fill in. Never commit .env.
#
# cp .env.example .env
# WOPI host allowed to open documents, as scheme://host:port. Further
# comma-separated entries are aliases of the same host.
COLLABORA_ALIASGROUP1=https://cloud.example.com:443
# Admin console login, at /browser/dist/admin/admin.html.
# Generate a password with: openssl rand -base64 24
COLLABORA_ADMIN_USERNAME=admin
COLLABORA_ADMIN_PASSWORD=
# Public hostname, without scheme.
COLLABORA_SERVER_NAME=office.example.com
# Spell-check languages loaded at start.
# COLLABORA_DICTIONARIES=de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru
-82
View File
@@ -1,82 +0,0 @@
# collabora
[Collabora Online](https://www.collaboraonline.com/code/) Development Edition
(CODE): an online office suite for documents, spreadsheets and presentations.
It holds no files itself; a WOPI host, a file server with Collabora
integration, opens documents in it.
One container: `collabora`.
## Setup
1. Set `COLLABORA_ALIASGROUP1` to the WOPI host's URL,
`COLLABORA_ADMIN_PASSWORD` and `COLLABORA_SERVER_NAME`.
2. Map the domain to port `9980` and deploy.
3. In the WOPI host, set the Collabora server URL to the public `https://`
address of this domain.
Discovery: `GET /hosting/discovery` returns XML once the server is up. The
image ships its own health check (`coolwsd --probe`, against `/livez`), so the
compose file declares none.
WebSocket editing sessions go through Traefik's default routing; no extra
labels are needed.
## Environment
| Variable | Default | Purpose |
| --- | --- | --- |
| `COLLABORA_ALIASGROUP1` | — | Allowed WOPI host, `scheme://host:port`; later comma-separated entries are aliases of it |
| `COLLABORA_ADMIN_USERNAME` / `COLLABORA_ADMIN_PASSWORD` | `admin` / — | Admin console at `/browser/dist/admin/admin.html` |
| `COLLABORA_SERVER_NAME` | empty | Public hostname; empty derives it from each request |
| `COLLABORA_DICTIONARIES` | optional | Space-separated spell-check languages; upstream's default list when unset |
`aliasgroup1` is the only access control on document editing: with no group
set, CODE trusts whichever host connects first after each start. Entries are
regular expressions, so `https://.*\.example\.com:443` allows a whole domain.
A second, unrelated WOPI host needs its own `aliasgroup2` line in
`compose.yml`.
`extra_params` is fixed in `compose.yml`: `--o:ssl.enable=false` serves plain
HTTP on `9980` for Traefik to terminate TLS in front of it, and
`--o:ssl.termination=true` makes CODE build `https://` and `wss://` URLs
anyway. Without it the editor loads over HTTPS but fails on mixed-content
WebSocket URLs.
The admin password fails fast if unset, because the console is served on the
public domain.
## Storage
None. Documents stay on the WOPI host; CODE's jails and cache are rebuilt on
every start.
## Isolation
CODE isolates each document process in a jail, choosing the first that works:
a mount namespace, then a chroot built by `coolforkit-caps`, then landlock.
The compose file adds no capabilities and no `security_opt`. Docker's default
seccomp profile blocks the `unshare` a mount namespace needs, so CODE falls back
to the chroot. `coolforkit-caps` carries file capabilities `CAP_CHOWN`,
`CAP_FOWNER` and `CAP_SYS_CHROOT`, all in Docker's default set, so it works
unprivileged. `MKNOD`, which older CODE images needed, is no longer used.
The price is speed, not safety: without `CAP_SYS_ADMIN` the `coolmount` helper
cannot bind-mount the system template, so each new jail copies it, and
opening a document takes a little longer. The alternatives cost more:
`cap_add: SYS_ADMIN` grants the container broad host-kernel powers, and
upstream's `cool-seccomp-profile.json`, Docker's default list plus the six
namespace and mount syscalls needed, must exist as a file on the host, which a Coolify compose deploy does
not place there.
The startup log line `creating usernamespace for mount user failed` is this
fallback, not a fault. Do not set `no-new-privileges`: `coolforkit-caps` gains
its capabilities on exec, and without them CODE drops to the weaker landlock
jail.
## Image
`collabora/code:latest` is the only moving tag upstream publishes; releases are
versioned `YY.MM.x`. The container keeps no state, so a new release needs only
a redeploy.
-11
View File
@@ -1,11 +0,0 @@
services:
collabora:
image: collabora/code:latest
restart: unless-stopped
environment:
- aliasgroup1=${COLLABORA_ALIASGROUP1:?required}
- extra_params=--o:ssl.enable=false --o:ssl.termination=true
- username=${COLLABORA_ADMIN_USERNAME:-admin}
- password=${COLLABORA_ADMIN_PASSWORD:?required}
- server_name=${COLLABORA_SERVER_NAME:-}
# - dictionaries=${COLLABORA_DICTIONARIES:-de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru}
+8 -4
View File
@@ -6,7 +6,7 @@ Self-hosted [Gitea](https://about.gitea.com/) backed by PostgreSQL.
| Service | Image | Internal port | Domain |
| --- | --- | --- | --- |
| `db` | `postgres:16-alpine` | 5432 | none |
| `db` | `postgres:18-alpine` | 5432 | none |
| `gitea` | `gitea/gitea:28` | 3000 | `GITEA_ROOT_URL` |
In Coolify, give `gitea` a domain on port 3000 matching `GITEA_ROOT_URL`.
@@ -35,8 +35,12 @@ itself.
this long.
- **`gitea/gitea:28`.** Gitea publishes major tags; the major pin takes
updates without a surprise major upgrade.
- **`postgres:16-alpine`** stays on 16: a new Postgres major cannot read the
existing data directory without a dump and restore.
- **`postgres:18-alpine`**, the newest Postgres major; Gitea needs 12 or
later. The major stays pinned because a new major cannot read an existing
data directory without a dump and restore. From 18 the image keeps its data
in `/var/lib/postgresql/18/docker`, so `db-data` mounts at
`/var/lib/postgresql`, which lets a later major sit beside it on the same
volume.
## Usage
@@ -48,5 +52,5 @@ token for any tool that mirrors into it.
| Volume | Holds |
| --- | --- |
| `db-data` | PostgreSQL data |
| `db-data` | PostgreSQL data, under `18/docker` |
| `gitea-data` | Repositories, Gitea config and state |
+2 -2
View File
@@ -1,13 +1,13 @@
services:
db:
image: postgres:16-alpine
image: postgres:18-alpine
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-gitea}
POSTGRES_USER: gitea
POSTGRES_DB: gitea
volumes:
- db-data:/var/lib/postgresql/data
- db-data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gitea -d gitea"]
interval: 5s
-10
View File
@@ -1,10 +0,0 @@
# Copy to .env and fill in. Never commit .env.
#
# cp .env.example .env
# Shared secret for signing editor requests; the storage app must use the same.
# Generate one with: openssl rand -hex 32
JWT_SECRET=
# Allow downloads from and callbacks to private IP addresses.
# ALLOW_PRIVATE_IP_ADDRESS=false
-60
View File
@@ -1,60 +0,0 @@
# onlyoffice
[ONLYOFFICE Docs](https://github.com/ONLYOFFICE/Docker-DocumentServer)
Community Edition: an online editor for documents, spreadsheets and
presentations. It has no file storage or user accounts of its own; a storage
app opens files in it and receives the saved result.
One container, `onlyoffice`. Map the domain to port `80`.
## Setup
1. Set `JWT_SECRET`.
2. Map the domain to port `80` and deploy. Every start regenerates the font
list, so the health check takes a minute or two to pass.
3. Give the storage app the public URL and the same `JWT_SECRET`.
Health check: `GET /healthcheck`. It answers `200` with `false` when a
dependency is down, so the compose healthcheck matches the body `true` rather
than relying on the status code.
## Environment
| Variable | Default | Purpose |
| --- | --- | --- |
| `JWT_SECRET` | — | Secret for signing requests between the editor and the storage app |
| `ALLOW_PRIVATE_IP_ADDRESS` | `false` | Allow fetching files from, and calling back to, private IP addresses |
`JWT_ENABLED` is fixed to `true`. `JWT_SECRET` fails fast if unset: the image
otherwise generates a random secret on every start, which breaks the storage
app's connection after each redeploy.
`ALLOW_PRIVATE_IP_ADDRESS` is needed only when the storage app is reached over
a private network, such as a container hostname or a LAN address. The editor
refuses those addresses by default, so leave it off when the storage app uses
a public URL.
## Storage
| Volume | Mount | Holds |
| --- | --- | --- |
| `onlyoffice-data` | `/var/www/onlyoffice/Data` | Certificates and generated WOPI keys |
| `onlyoffice-lib` | `/var/lib/onlyoffice` | Document cache and converted files |
| `onlyoffice-logs` | `/var/log/onlyoffice` | Logs |
Nothing here is the documents themselves; those stay in the storage app. The
volumes keep the cache and keys across redeploys.
## Images
`onlyoffice/documentserver:latest`: upstream publishes `X.Y` and `X.Y.Z` tags
but no major tag. Back up the volumes before a pull that crosses a major.
The 9.x images up to 9.4 still run PostgreSQL, RabbitMQ and Redis inside the
container for the Community Edition, in volumes the image declares itself;
upstream's source has since dropped them from the Community build. Either way
no separate database, broker or cache container is needed, and none of their
connection variables are set.
`stop_grace_period: 60s` follows upstream's compose, giving the editor time to
save open documents back to the storage app on shutdown.
-24
View File
@@ -1,24 +0,0 @@
services:
onlyoffice:
image: onlyoffice/documentserver:latest
restart: unless-stopped
stop_grace_period: 60s
environment:
- JWT_ENABLED=true
- JWT_SECRET=${JWT_SECRET:?required}
# - ALLOW_PRIVATE_IP_ADDRESS=${ALLOW_PRIVATE_IP_ADDRESS:-false}
volumes:
- onlyoffice-data:/var/www/onlyoffice/Data
- onlyoffice-lib:/var/lib/onlyoffice
- onlyoffice-logs:/var/log/onlyoffice
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost/healthcheck | grep -qx true"]
interval: 30s
timeout: 10s
retries: 5
start_period: 120s
volumes:
onlyoffice-data:
onlyoffice-lib:
onlyoffice-logs:
+25 -8
View File
@@ -1,6 +1,6 @@
# owncloud
[ownCloud](https://doc.owncloud.com/server/10.16/admin_manual/installation/docker/)
[ownCloud](https://doc.owncloud.com/server/11.0/admin_manual/installation/installing_with_docker.html)
Server: file sync and share, with web, desktop and mobile clients.
Three containers: `owncloud`, `db` (MariaDB) for metadata, users and shares, and
@@ -15,7 +15,11 @@ Three containers: `owncloud`, `db` (MariaDB) for metadata, users and shares, and
3. Log in with `OWNCLOUD_ADMIN_USERNAME` / `OWNCLOUD_ADMIN_PASSWORD`.
Health check: `/usr/bin/healthcheck`, the image's own script, also the compose
healthcheck.
healthcheck. It requests `/status.php` on port `8080`.
Every start runs `occ upgrade` when `config.php` on `owncloud-data` says
ownCloud is installed, and a fresh install otherwise. A new image version is
therefore migrated on the next deploy, against whatever database `db` holds.
## Environment
@@ -29,6 +33,10 @@ healthcheck.
`OWNCLOUD_DOMAIN` also fills `OWNCLOUD_TRUSTED_DOMAINS`; ownCloud rejects
requests for any host not on that list.
TLS ends at the proxy, so ownCloud sees plain HTTP. `OWNCLOUD_OVERWRITE_PROTOCOL`
makes the URLs it generates `https`, and `OWNCLOUD_PROTOCOL` does the same for
the URL `occ` and cron jobs use in links.
The admin variables are read only by the first-start install. Changing them
later does not change the account; use the web UI or `occ user:resetpassword`.
@@ -50,10 +58,19 @@ named one Docker creates an anonymous volume on every recreate.
## Images
`owncloud/server:10` tracks the 10.x line. `latest` points at the same
release today, but ownCloud now also publishes 11.x, and a move to a new major
should be a deliberate upgrade with a backup, not a side effect of a pull.
`owncloud/server:11` tracks the 11.x line, so patch releases arrive on the next
deploy and a move to a new major stays a deliberate upgrade. ownCloud's own
compose pins a full version instead.
`mariadb:12` and `redis:6` track their major versions. A MariaDB data
directory cannot move back to an older major, so back up the database volume
before changing it.
`mariadb:10.11` is the release ownCloud 11's compose uses and the top of the
MariaDB range ownCloud supports (10.2 to 10.11); a newer major is outside it.
`MARIADB_AUTO_UPGRADE=1`, also from ownCloud's compose, runs `mariadb-upgrade`
when the server version changes. A MariaDB data directory cannot move back to
an older major, so going down a version means a dump and restore into an empty
volume, not an image change. Back up `db-data` before changing the tag.
`redis:7` is the version ownCloud's compose uses.
ownCloud 11 runs on PHP 8. Apps installed into `/mnt/data/apps` from the
marketplace under 10.x were built for PHP 7, so they are removed or replaced
with PHP 8 versions before moving a 10.x install to 11.
+6 -3
View File
@@ -1,10 +1,12 @@
services:
owncloud:
image: owncloud/server:10
image: owncloud/server:11
restart: unless-stopped
environment:
- OWNCLOUD_DOMAIN=${OWNCLOUD_DOMAIN:?required}
- OWNCLOUD_TRUSTED_DOMAINS=${OWNCLOUD_DOMAIN:?required}
- OWNCLOUD_PROTOCOL=https
- OWNCLOUD_OVERWRITE_PROTOCOL=https
- OWNCLOUD_ADMIN_USERNAME=${OWNCLOUD_ADMIN_USERNAME:-admin}
- OWNCLOUD_ADMIN_PASSWORD=${OWNCLOUD_ADMIN_PASSWORD:?required}
- OWNCLOUD_DB_TYPE=mysql
@@ -30,7 +32,7 @@ services:
# MariaDB for file metadata, users and shares.
db:
image: mariadb:12
image: mariadb:10.11
restart: unless-stopped
command:
- --character-set-server=utf8mb4
@@ -42,6 +44,7 @@ services:
- MYSQL_DATABASE=${DB_NAME:-owncloud}
- MYSQL_USER=${DB_USER:-owncloud}
- MYSQL_PASSWORD=${DB_PASSWORD:?required}
- MARIADB_AUTO_UPGRADE=1
volumes:
- db-data:/var/lib/mysql
healthcheck:
@@ -52,7 +55,7 @@ services:
# Redis for file locking and the distributed cache.
cache:
image: redis:6
image: redis:7
restart: unless-stopped
command: ["--databases", "1"]
volumes:
-21
View File
@@ -1,21 +0,0 @@
# Copy to .env and fill in. Never commit .env.
#
# cp .env.example .env
# Public hostname, without scheme.
SEAFILE_SERVER_HOSTNAME=seafile.example.com
# Shared secret between Seafile and the notification server, 32+ characters.
# Generate one with: openssl rand -hex 32
JWT_PRIVATE_KEY=
# Admin account, created on first start only.
INIT_SEAFILE_ADMIN_EMAIL=admin@example.com
INIT_SEAFILE_ADMIN_PASSWORD=
# MariaDB passwords. Seafile uses the root password on first start to create
# the seafile user and its three databases.
DB_PASSWORD=
DB_ROOT_PASSWORD=
TIME_ZONE=Etc/UTC
-98
View File
@@ -1,98 +0,0 @@
# seafile
[Seafile](https://manual.seafile.com/13.0/) Community Edition 13: file sync
and share with libraries, web, desktop and mobile clients. Based on the
official 13.0 Docker compose, without its bundled Caddy; the platform proxy
terminates TLS.
Four containers: `seafile` (Seahub web UI and file server), `notification`
(real-time change notifications over WebSocket), `db` (MariaDB) and `cache`
(Redis).
## Setup
1. Set `SEAFILE_SERVER_HOSTNAME`, `JWT_PRIVATE_KEY`, `INIT_SEAFILE_ADMIN_EMAIL`,
`INIT_SEAFILE_ADMIN_PASSWORD`, `DB_PASSWORD` and `DB_ROOT_PASSWORD`.
2. Map the domains, both on the same host:
| Container | Domain | Port |
| --- | --- | --- |
| `seafile` | `https://seafile.example.com` | `80` |
| `notification` | `https://seafile.example.com/notification` | `8083` |
Keep the app's strip-prefix setting on (the default), so the notification
server receives `/` rather than `/notification`.
3. Deploy. The first start creates the databases and the admin account; log in
with `INIT_SEAFILE_ADMIN_EMAIL` / `INIT_SEAFILE_ADMIN_PASSWORD`.
Health check: `curl -f http://localhost:80` inside `seafile`, from the official
compose. Its start period is two minutes instead of the official ten seconds:
the first start creates the databases before Seahub answers, and
`notification` waits for `seafile` to be healthy.
## Environment
| Variable | Default | Purpose |
| --- | --- | --- |
| `SEAFILE_SERVER_HOSTNAME` | — | Public hostname, without scheme; also builds the notification URL |
| `JWT_PRIVATE_KEY` | — | Shared secret between `seafile` and `notification`, 32+ characters |
| `INIT_SEAFILE_ADMIN_EMAIL` / `INIT_SEAFILE_ADMIN_PASSWORD` | — | Admin account, first start only |
| `DB_PASSWORD` | — | Password of the `seafile` MariaDB user |
| `DB_ROOT_PASSWORD` | — | MariaDB root password, used by the first start to create the user and databases |
| `TIME_ZONE` | `Etc/UTC` | Server time zone |
`SEAFILE_SERVER_PROTOCOL` is fixed to `https`: TLS ends at the proxy, but the
generated links, CSRF origins and notification URL must use the public scheme.
The `INIT_*` variables and `DB_ROOT_PASSWORD` take effect only on the first
start. Changing them later does not change the admin account or the database
passwords.
Logs go to stdout (`SEAFILE_LOG_TO_STDOUT=true`) so they show in the
platform's log view instead of only under `/shared/seafile/logs`.
## Storage
| Volume | Mount | Holds |
| --- | --- | --- |
| `seafile-data` | `/shared` | Libraries, config, logs |
| `db-data` | `/var/lib/mysql` | The ccnet, seafile and seahub databases |
| `cache-data` | `/data` | Redis cache |
`notification` has no volume: it reads its settings from the environment and,
with `SEAFILE_LOG_TO_STDOUT=true`, writes no log file, so the log mount of the
official compose is not needed.
The `redis` image declares `/data` a volume, so without a named one Docker
creates an anonymous volume on every recreate.
## Left out
- **SeaDoc** (`ENABLE_SEADOC=false`). It needs `/sdoc-server/` with the prefix
stripped and `/socket.io/` with it kept, on the same host. The proxy's
strip-prefix setting applies to the whole app, so both cannot be routed at
once.
- **Thumbnail server.** It needs `/thumbnail/` unstripped and
`/thumbnail/ping` rewritten to `/ping`, the same conflict. Without it, Seahub
generates thumbnails itself.
- **SeaSearch, Seafile AI, metadata server.** Not needed for file sync;
SeaSearch publishes no arm64 image.
- **Redis password.** The official compose passes an empty one by default;
Redis is reachable only on the app's internal network.
## Images
`seafileltd/seafile-mc:13.0-latest` and
`seafileltd/notification-server:13.0-latest` track the 13.0 line. Seafile
publishes no `:13` tag, `latest` has not moved since 2025, and 14 is still a
testing release. A Seafile major upgrade runs database migrations; back up
both volumes first.
`mariadb:10.11` is the version the official 13.0 compose pins, and the tag
follows its patch releases. `MARIADB_AUTO_UPGRADE=1` (from the official compose) runs
`mariadb-upgrade` after a minor update. A MariaDB data directory cannot move
back to an older major.
`redis:8` is the major the official compose's unpinned `redis` resolves to
today; the tag keeps it there instead of following a future major with no
review. Redis holds only cache, so a version change loses nothing.
-93
View File
@@ -1,93 +0,0 @@
services:
seafile:
image: seafileltd/seafile-mc:13.0-latest
restart: unless-stopped
environment:
- SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME:?required}
- SEAFILE_SERVER_PROTOCOL=https
- JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?required}
- INIT_SEAFILE_ADMIN_EMAIL=${INIT_SEAFILE_ADMIN_EMAIL:?required}
- INIT_SEAFILE_ADMIN_PASSWORD=${INIT_SEAFILE_ADMIN_PASSWORD:?required}
- SEAFILE_MYSQL_DB_HOST=db
- SEAFILE_MYSQL_DB_PORT=3306
- SEAFILE_MYSQL_DB_USER=seafile
- SEAFILE_MYSQL_DB_PASSWORD=${DB_PASSWORD:?required}
- INIT_SEAFILE_MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD:?required}
- SEAFILE_MYSQL_DB_CCNET_DB_NAME=ccnet_db
- SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=seafile_db
- SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=seahub_db
- CACHE_PROVIDER=redis
- REDIS_HOST=cache
- REDIS_PORT=6379
- ENABLE_NOTIFICATION_SERVER=true
- INNER_NOTIFICATION_SERVER_URL=http://notification:8083
- NOTIFICATION_SERVER_URL=https://${SEAFILE_SERVER_HOSTNAME:?required}/notification
- ENABLE_SEADOC=false
- TIME_ZONE=${TIME_ZONE:-Etc/UTC}
- SEAFILE_LOG_TO_STDOUT=true
volumes:
- seafile-data:/shared
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 2m
# Notification server: pushes library changes to web and desktop clients over WebSocket.
notification:
image: seafileltd/notification-server:13.0-latest
restart: unless-stopped
environment:
- JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?required}
- SEAFILE_MYSQL_DB_HOST=db
- SEAFILE_MYSQL_DB_PORT=3306
- SEAFILE_MYSQL_DB_USER=seafile
- SEAFILE_MYSQL_DB_PASSWORD=${DB_PASSWORD:?required}
- SEAFILE_MYSQL_DB_CCNET_DB_NAME=ccnet_db
- SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=seafile_db
- SEAFILE_LOG_TO_STDOUT=true
depends_on:
db:
condition: service_healthy
seafile:
condition: service_healthy
# MariaDB for the ccnet, seafile and seahub databases.
db:
image: mariadb:10.11
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD:?required}
- MYSQL_LOG_CONSOLE=true
- MARIADB_AUTO_UPGRADE=1
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--mariadbupgrade", "--innodb_initialized"]
interval: 20s
start_period: 30s
timeout: 5s
retries: 10
# Redis for the Seahub cache.
cache:
image: redis:8
restart: unless-stopped
volumes:
- cache-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
seafile-data:
db-data:
cache-data: