Import complete: 25 044 objs / 26 GB. 99.97% залито rclone'ом (22.2 GB за ~2.5 ч), оставшиеся 6 файлов (4.04 GB) — 4× .pat ~221 MB + 2× .seospider 1.55+1.7 GB — упали с 502/500 на каждом rclone PUT из-за 60-секундного HTTP timeout в reva v2.27 datagateway (hardcoded в Go http.Client.Timeout, не env-configurable). Workaround: VDS-side curl PUT loopback через throwaway sftp key. Local network 142 MB/s → 4.04 GB за 28 секунд, все PROPFIND size match. Wiki: §Gotcha 5 в concepts/ocis-on-vds-deploy-recipe.md документирует finding + recipe для workaround. Traefik buffering middleware пробован, не помог (likely vulcand/oxy buffer bug на больших телах) — откатил. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
137 lines
11 KiB
Markdown
137 lines
11 KiB
Markdown
# owncloud-vds-deploy
|
||
|
||
## Goal
|
||
|
||
Поднять [[owncloud-infinite-scale]] (oCIS 7.1.0) на vds-kzntsv в качестве замены мёртвого ownCloud сервера на Synology NAS (см. [[nas-recovery]]). После восстановления — импортировать 24.7 GB из off-VDS backup-копии. Сервис на `owncloud.kzntsv.site`, files-only (calendar/contacts не нужны).
|
||
|
||
## Decisions log
|
||
|
||
- **2026-05-21 — oCIS vs Seafile vs Nextcloud:** oCIS выбран. Reasons: (1) drop-in миграция — decomposedfs storage по сути plain-files на диске (24.7 GB просто кладутся); (2) lightweight — single Go binary, не PHP-monstr classic ownCloud 10; (3) workflow continuity — user уже знаком с ownCloud UX; (4) тривиальный backup (tar `data/` + конфиг); (5) future-proof — oCIS = main line от ownCloud GmbH, classic OC10 deprecating. Seafile отвергнут: block-storage = нет drop-in import, медленнее migration. Nextcloud отвергнут: только files нужны — calendar/contacts overhead не оправдан.
|
||
|
||
- **2026-05-21 — `user: "1001:1001"` override:** image owncloud/ocis runs as UID 1000 (ocis-user inside container) by default, но host vitya = UID 1001. Mount-target dirs (./data, ./config) owned by vitya → container не мог писать. Fix: добавил `user: "1001:1001"` в compose — container runs as host UID, owns mounted dirs. Альтернатива (`chown 1000` хост-dirs) отвергнута — создаёт orphan UID на хосте.
|
||
|
||
- **2026-05-21 — `PROXY_ENABLE_BASIC_AUTH=true`:** дефолтно oCIS basic auth выключен (OIDC-only). Для WebDAV-клиентов (rclone, ownCloud desktop) и LibreGraph API admin-операций basic auth нужен → enabled. Trade-off: чуть слабее security (basic auth не имеет MFA/refresh tokens), но threat model personal use acceptable.
|
||
|
||
- **2026-05-21 — `PROXY_TLS=false`:** docs прямо разрешают, когда TLS термируется на reverse proxy (traefik certresolver letsEncrypt). Альтернатива — собственный self-signed cert внутри контейнера + traefik passthrough — overhead без выгоды.
|
||
|
||
- **2026-05-21 — admin/user-vitya passwords в pass-store:** `owncloud/admin-password` (32 chars random) + `owncloud/user-vitya` (32 chars random). Encrypted-at-rest, cross-machine sync через `OpeItcLoc03/password-store-private`. Закрывает риски [[secrets-manager-adopt]].
|
||
|
||
- **2026-05-21 — DNS surprise:** ожидали, что user будет создавать A-запись `owncloud.kzntsv.site → 89.253.255.94` вручную. Reality: запись уже была (видимо ранее создана при wildcard cleanup / dns prep), сразу резолвилась на VDS_IP. Wildcard `*.kzntsv.site → 94.19.247.14` (NAS recovery host) overlay-ится specific записями.
|
||
|
||
- **2026-05-21 — backup integration:** добавил `/opt/stacks/owncloud` в rsync source list `/opt/stacks/backup/scripts/run.sh` ([[vds-backup-rsync-kreknin]]). После import 24.7GB первый full backup snapshot будет +24.7GB на kreknin, далее ~hardlink incremental. kreknin 5.6 TB free — comfortably fits.
|
||
|
||
## Implementation sketch
|
||
|
||
`/opt/stacks/owncloud/docker-compose.yml`:
|
||
|
||
```yaml
|
||
services:
|
||
ocis:
|
||
image: owncloud/ocis:7.1.0
|
||
container_name: owncloud
|
||
restart: unless-stopped
|
||
user: "1001:1001"
|
||
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"
|
||
PROXY_ENABLE_BASIC_AUTH: "true"
|
||
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-char-random — стор в pass owncloud/admin-password>
|
||
```
|
||
|
||
User creation via LibreGraph API (`POST /graph/v1.0/users`, basic auth as admin).
|
||
|
||
## Completed steps
|
||
|
||
- [x] **Compose** написан, image `owncloud/ocis:7.1.0` pulled (digest `sha256:c75af880...b717b`)
|
||
- [x] **Dirs:** `/opt/stacks/owncloud/{data,config}` (vitya:vitya 775)
|
||
- [x] **Secrets:** `OCIS_ADMIN_PASSWORD` в `.env` (chmod 600), originals в pass-store: `owncloud/admin-password` + `owncloud/user-vitya` (pushed to OpeItcLoc03/password-store-private)
|
||
- [x] **Deploy:** `docker compose up -d` через ssh (limited stack — Portainer auto-detects)
|
||
- [x] **UID fix:** `user: "1001:1001"` после первого "permission denied" на `/etc/ocis/ocis.yaml`
|
||
- [x] **Basic auth fix:** `PROXY_ENABLE_BASIC_AUTH=true` после 401 на LibreGraph API
|
||
- [x] **Traefik integration:** labels подхвачены, cert via letsEncrypt valid, HTTPS 200 OK
|
||
- [x] **User `vitya` created** через LibreGraph API (`POST /graph/v1.0/users`, id `58110e99-5e92-43f8-a077-9f2ec7f5562e`)
|
||
- [x] **WebDAV verified:** `PROPFIND /dav/files/vitya/` returns 200 multistatus с personal space "Vitya" + system space "Shares"
|
||
- [x] **Backup integration:** `/opt/stacks/owncloud` добавлен в rsync source list `/opt/stacks/backup/scripts/run.sh` (между `/opt/stacks/backup` и `/etc/ssh`). `bash -n` syntax OK. Next nightly run 05:00 MSK захватит config + (post-import) data.
|
||
|
||
## Closed 2026-05-22 — import complete
|
||
|
||
25 044 объектов / **26 GB** на vitya space. Path: 22.2 GB rclone (3.35 MiB/s uplink, ~2.5h) + 4.04 GB VDS-side curl loopback (6 файлов с 502/500 from 60s timeout — см. §Closed-out finding ниже).
|
||
|
||
Все PROPFIND size match. Backup integration: `/opt/stacks/owncloud` в `vds-backup-rsync-kreknin` rsync sources — next snapshot 2026-05-22 05:00 MSK захватит 26 GB.
|
||
|
||
### Closed-out finding: 60s HTTP upload timeout
|
||
|
||
При rclone uplink ~3.35 MiB/s упали все файлы ≥221 MB (4× .pat 220-222 MB, 508 MB zip, 1.55 + 1.7 GB seospider). Logs traefik показывали ровно `60000ms` duration на 502. oCIS error log: `Put "http://localhost:9158/data/simple/...": context canceled`, `time_ns:59944479090` (59.94s) — internal HTTP client между oCIS services вырубает request на 60s. Hardcoded в Go `http.Client.Timeout` где-то в reva v2.27 datagateway.go:200. Env-var fix отсутствует.
|
||
|
||
**Traefik buffering middleware попробован, не помог:** `traefik.http.middlewares.oc-buffering.buffering.maxRequestBodyBytes=0` + `memRequestBodyBytes=1048576` через docker labels. Traefik сам стал выдавать 500 на 60s mark (backend_url="-" в access log = до oCIS не доходило). Likely bug в `vulcand/oxy` buffer на больших телах. Откатил.
|
||
|
||
**Workaround:** VDS-side direct curl PUT loopback'ом через traefik. Пользовательский агент:
|
||
1. Throwaway ed25519 key сгенерирован на VDS, append'нут как `restrict,command="internal-sftp"` в `~/.ssh/authorized_keys` (sftp-only, no shell).
|
||
2. Агент `sftp -i <key> -b <batch> vitya@VDS` залил 6 файлов в `/tmp/oc-import/` (~20 мин на агентовом uplink).
|
||
3. С VDS — `for SRC ...; curl -T $SRC <ocis-url-with-urlencode>` через localhost → traefik → owncloud. Local network 142 MB/s — все 4.04 GB за 28 секунд (1.7 GB файл за 10s).
|
||
4. Verify PROPFIND `<oc:size>` match `stat -c %s`. All 6 OK.
|
||
5. Cleanup: `rm -rf /tmp/oc-import/`, throwaway-key revoked из authorized_keys.
|
||
|
||
Pattern закреплён в [`.wiki/concepts/ocis-on-vds-deploy-recipe.md`](../.wiki/concepts/ocis-on-vds-deploy-recipe.md) §Gotcha 5.
|
||
|
||
## Skipped / not-our-bug
|
||
|
||
- 2× DipTrace `.exe` (~2 MB total) — заблокированы Windows Defender на агентовой машине при copy в staging. Не баг oCIS. Не блокер.
|
||
- Portainer API key invalid (401) — не блокировало deploy (admin user/pass работали), требует regen через UI + pass-store update если кто будет использовать API.
|
||
|
||
## Open (legacy section) — DEPRECATED, см. closed-out выше
|
||
|
||
- [x] ~~Import 24.7 GB~~ — done 2026-05-22 (rclone + VDS-side curl)
|
||
```
|
||
rclone copy "<src>" :webdav: \
|
||
--webdav-url https://owncloud.kzntsv.site/dav/files/vitya \
|
||
--webdav-vendor owncloud \
|
||
--webdav-user vitya \
|
||
--webdav-pass $(rclone obscure "$(pass show owncloud/user-vitya)") \
|
||
--progress --transfers 4 --checkers 4 --tpslimit 8 \
|
||
--retries 5 --low-level-retries 10 --stats 30s
|
||
```
|
||
Resumable. Прогресс мониторится с VDS: `ssh vitya@VDS 'du -sh /opt/stacks/owncloud/data/spaces/'`.
|
||
|
||
- [ ] **Post-import verify:** size match (≈24.7 GB на data/) + spot-check файлов через web UI.
|
||
|
||
- [ ] **Pin image version** — сейчас `7.1.0` явно (good). Consider tracking 7.x patch releases — но не auto-update на 8.x без manual verify (breaking config schema changes возможны).
|
||
|
||
- [ ] **API-key rotate?** Portainer api-key в `vds-kzntsv/full-env` оказался invalid (401 "Invalid JWT token"). Не блокирует — admin user/pass работают. Follow-up: regenerate API key через Portainer UI + update pass-store. Отдельная chore-task если нужно.
|
||
|
||
## Notes
|
||
|
||
- **Architecture:** request flow → DNS owncloud.kzntsv.site → 89.253.255.94 → ufw 80/443 → traefik websecure → owncloud (172.18.0.14:9200) → decomposedfs storage в `/var/lib/ocis` mount → host `/opt/stacks/owncloud/data/`.
|
||
- **Storage layout:** oCIS использует decomposedfs — каждый файл = node с metadata. Spaces (personal + Shares) живут в `data/spaces/<space-uuid>/`. Backup-friendly: plain files на диске, не block storage.
|
||
- **Atomic revert:**
|
||
```
|
||
ssh vitya@89.253.255.94 'cd /opt/stacks/owncloud && docker compose down -v && cd .. && rm -rf owncloud'
|
||
# + remove /opt/stacks/owncloud line из run.sh
|
||
# + pass rm owncloud/admin-password owncloud/user-vitya && pass git push
|
||
```
|
||
- **No external DB** — oCIS использует internal KV-store (`data/storage-users/`, `data/idm/`) для metadata. Postgres/MariaDB на VDS не задействованы. Это снимает зависимость от shared-dbs network — owncloud только в `proxy`.
|