New concept page documents the oCIS-on-VDS deploy pattern with the 4
non-obvious gotchas surfaced during owncloud-vds-deploy:
1. UID mismatch — image ocis-user is 1000, host vitya is 1001 →
compose `user: "1001:1001"` override (alt chown rejected as orphan-UID)
2. PROXY_TLS=false — explicit per docs when reverse proxy terminates HTTPS
3. PROXY_ENABLE_BASIC_AUTH=true — required for WebDAV/LibreGraph API
4. LibreGraph POST /graph/v1.0/users — only way to create users (no CLI)
Plus decomposedfs storage layout, atomic-revert recipe, backup integration
note. Source: .tasks/owncloud-vds-deploy.md.
entities/vds-kzntsv.md updated: stack table + hostnames + file layout
include owncloud row.
index.md catalog + log.md ingest entry refreshed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.3 KiB
title, type, tags, sources, updated
| title | type | tags | sources | updated | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| oCIS на VDS — deploy recipe + non-obvious gotchas | concept |
|
|
2026-05-21 |
oCIS на VDS — deploy recipe + non-obvious gotchas
Recipe для разворачивания ownCloud Infinite Scale (oCIS) — Go-based замены classic ownCloud Server 10 — в Docker за traefik с LetsEncrypt. Применимо на vds-kzntsv и других хостах с тем же pattern'ом (/opt/stacks/<name>/, traefik network proxy, LE certresolver letsEncrypt).
Validated 2026-05-21 на image owncloud/ocis:7.1.0. Container запустился, traefik routes 200 OK, LibreGraph API + WebDAV работают, init создаёт admin + idm.json в data/idm/.
Когда применять
- Личный personal-cloud для одного пользователя (single binary, no external DB)
- Replace мёртвого / legacy ownCloud Server 10 (oCIS = main line от ownCloud GmbH, OC10 deprecating)
- Files-only use-case (calendar/contacts не нужны — для тех нужен Nextcloud)
- На хосте с уже работающим traefik + LE certresolver (recipe рассчитан на termination upstream)
Когда не применять
- Multi-tenant корпоративная инсталляция → нужны external IDP, OIDC chain, не личный setup
- Нужны calendar / contacts / mail / collabora → Nextcloud, не oCIS
- Block-level dedup критичен → Seafile (oCIS использует file-level decomposedfs)
- Host UID = 1000 (image uid 1000 matches →
user:override не нужен)
Compose recipe
/opt/stacks/owncloud/docker-compose.yml:
services:
ocis:
image: owncloud/ocis:7.1.0
container_name: owncloud
restart: unless-stopped
user: "1001:1001" # ← gotcha 1, см. ниже
entrypoint: /bin/sh
command: ["-c", "ocis init || true; exec ocis server"]
environment:
OCIS_URL: https://owncloud.kzntsv.site
OCIS_LOG_LEVEL: info
OCIS_LOG_COLOR: "false"
PROXY_TLS: "false" # ← gotcha 2
PROXY_ENABLE_BASIC_AUTH: "true" # ← gotcha 3
OCIS_INSECURE: "false"
IDM_CREATE_DEMO_USERS: "false"
IDM_ADMIN_PASSWORD: ${OCIS_ADMIN_PASSWORD}
volumes:
- ./data:/var/lib/ocis
- ./config:/etc/ocis
networks: [proxy]
labels:
- traefik.enable=true
- "traefik.http.routers.owncloud.rule=Host(`owncloud.kzntsv.site`)"
- traefik.http.routers.owncloud.entrypoints=websecure
- traefik.http.routers.owncloud.tls.certresolver=letsEncrypt
- traefik.http.services.owncloud.loadbalancer.server.port=9200
networks:
proxy: { external: true }
/opt/stacks/owncloud/.env (chmod 600):
OCIS_ADMIN_PASSWORD=<32+ random chars, store в pass owncloud/admin-password>
Gotcha 1: UID mismatch image vs host
Image owncloud/ocis runs as UID 1000 (ocis-user внутри контейнера). Если host-side user-owner mounted dirs ≠ 1000 — контейнер падает в restart-loop с:
Could not create config: open /etc/ocis/ocis.yaml: permission denied
The jwt_secret has not been set properly in your config for ocis.
«jwt_secret has not been set» — обманка, симптом сидит на ровном месте perm-deny при попытке записать ocis.yaml на первом ocis init пробеге.
Fix: user: "<host-uid>:<host-gid>" override в compose. На vds-kzntsv host user vitya = UID 1001 → user: "1001:1001". Альтернатива (chown 1000 host-dirs) отвергнута: создаёт orphan UID на хосте, путает auditing + backup tools.
Diagnose UID: stat -c "%u:%g %U:%G" /opt/stacks/owncloud/config.
Gotcha 2: PROXY_TLS=false за reverse proxy
Дефолт oCIS — PROXY_TLS=true (HTTPS на proxy port 9200 self-signed). За traefik, который сам терминирует HTTPS, это даёт двойную TLS-обёртку — traefik пытается сделать backend TLS, не получается.
Per ownCloud docs: «If a reverse proxy is used to terminate HTTPS, PROXY_TLS can be set to false, though this means communication between the proxy and Infinite Scale will be unencrypted.» На том же хосте, в bridge network — acceptable.
Gotcha 3: PROXY_ENABLE_BASIC_AUTH=true для WebDAV + admin API
Дефолтно oCIS — OIDC-only (browser-based login). Для rclone / ownCloud-desktop / curl LibreGraph API нужен basic auth — иначе 401 везде, включая GET /graph/v1.0/me.
Trade-off: basic auth не имеет MFA/refresh-tokens. Для personal use acceptable. Для shared deployment — обернуть LDAP / OIDC отдельно.
Gotcha 4: User-create via LibreGraph API (нет CLI)
oCIS не имеет ocis idm user add или подобной CLI команды. Создание users — только через:
- Web UI как admin → Settings → Users
- LibreGraph API:
POST /graph/v1.0/usersс basic auth admin'а
API recipe:
curl -u "admin:$ADMIN_PWD" -X POST \
"https://owncloud.kzntsv.site/graph/v1.0/users" \
-H "Content-Type: application/json" \
-d '{
"displayName": "Vitya",
"onPremisesSamAccountName": "vitya",
"mail": "vitya@kzntsv.site",
"passwordProfile": {"password": "<random-32>"},
"accountEnabled": true
}'
→ HTTP 201 с user JSON (с id UUID).
List users: GET /graph/v1.0/users.
Storage layout (decomposedfs)
После ocis init data dir выглядит так:
/opt/stacks/owncloud/data/
├── idm/ — internal IDM (LDAP-style) state
├── idp/ — OIDC IdP private keys (private-key.pem etc)
├── nats/ — internal pub-sub messaging
├── search/ — indexer state (bleve)
└── storage/
├── metadata/spaces/<2>/<rest>/ — space metadata
└── users/spaces/<2>/<rest>/ — actual user files (decomposedfs nodes)
Каждый user space = <2-char-prefix>/<26-char-rest-of-uuid>/ (split UUID для FS spread). Files внутри хранятся как plain bytes + sidecar metadata (xattrs / .meta.json). Backup-friendly: tar+rsync захватит всё корректно, восстановление = просто разархивировать обратно.
Internal KV-store (storage/users/spaces/.../metadata/, idm/idm.json, etc.) — JSON files, не БД. Это и есть «no external DB needed» — oCIS использует filesystem + NATS вместо Postgres/Redis.
Backup integration
/opt/stacks/owncloud (whole dir) добавлен в rsync source list vds-backup-rsync-kreknin:
/opt/stacks/backup \
+ /opt/stacks/owncloud \
/etc/ssh \
Live container во время rsync — acceptable trade-off (decomposedfs использует atomic rename, отдельные node-files не corrupt'ятся). Если требуется strictly consistent snapshot — docker stop owncloud → rsync → docker start (downtime ~30s).
Первый full backup snapshot после import → +~25 GB на kreknin. Далее hardlink-incremental ≈ 1 GB/day.
Connection chain
DNS owncloud.kzntsv.site → 89.253.255.94 (VDS_IP)
→ ufw 443
→ traefik websecure (LE cert via letsEncrypt resolver)
→ docker network proxy (172.18.0.0/16)
→ container owncloud:9200 (oCIS proxy service)
→ internal services: idp / idm / users / groups / proxy / storage-system / sharing / search ...
Atomic revert
ssh vitya@89.253.255.94 'cd /opt/stacks/owncloud && docker compose down -v && cd .. && rm -rf owncloud'
# rsync source list cleanup (revert):
ssh vitya@89.253.255.94 "sed -i '\\|/opt/stacks/owncloud|d' /opt/stacks/backup/scripts/run.sh"
# secrets cleanup:
pass rm owncloud/admin-password owncloud/user-vitya && pass git push
Related
- admin-infra-project — общий context для admin-infra repo
- vds-kzntsv — host entity (owncloud стек добавлен в его software stack)
- future-resilient-architecture-goals — RTO/RPO targets (oCIS — single-user, RTO/RPO defer)