8 min read

ownCloud Infinite Scale (OCIS) on alpine incus container

ownCloud Infinite Scale (OCIS) on alpine incus container

Deploying ownCloud Infinite Scale (oCIS) inside an Alpine Linux container on Incus is an exceptional choice. It aligns perfectly with a minimalist, high-performance UNIX philosophy: Incus handles system containerization efficiently, Alpine minimizes OS overhead, and oCIS completely ditches old, heavy PHP/Apache dependencies in favor of a fast, single compiled Go binary.

Here is the complete step-by-step blueprint to build, configure, and launch this setup.

1. Host Setup: Create the Alpine Incus Container

First, launch a clean Alpine Linux container inside Incus. We will use the privileged=true or pass-through settings only if you plan to mount massive external storage pools (like ZFS datasets or your local IPFS drives). For standard setups, an unprivileged container is perfect.

Bash

# Launch the Alpine container (using the latest stable image)
incus launch images:alpine/edge ocis-container

# Configure the container to start automatically with the host
incus config set ocis-container boot.autostart true

Optional: If you want to give the container direct access to a specific fast storage directory on your host (e.g., /mnt/fast-storage), map it before booting into the container shell:

Bash

incus config device add ocis-container storage-disk disk source=/mnt/fast-storage path=/var/lib/ocis

2. Base Container OS Preparation

Enter your new container's root shell to configure dependencies and network tools:

Bash

incus exec ocis-container -- /bin/sh

Inside the container, run the update and install basic system packages (libcap helps manage binary ports safely, and curl pulls the binary):

Bash

apk update && apk upgrade
apk add curl tzdata ca-certificates libcap

3. Install ownCloud Infinite Scale (oCIS)

Because oCIS is written entirely in Go, we don't need a heavy Docker layer or PHP modules inside Alpine. We can drop the official Linux binary straight onto the filesystem.

Bash

# Create a dedicated system user and group for security
addgroup -S ocis && adduser -S ocis -G ocis -h /var/lib/ocis -s /sbin/nologin

# Fetch the official oCIS Linux-amd64 binary
# (Verify the latest version string on ownCloud's download platform)
curl -L -o /usr/local/bin/ocis https://download.owncloud.com/ocis/ocis/stable/3.0.0/ocis-3.0.0-linux-amd64

# Grant execution rights
chmod +x /usr/local/bin/ocis

# Allow the binary to bind to low ports (like 443/80) without running as absolute root
setcap 'cap_net_bind_service=+ep' /usr/local/bin/ocis

4. Initialize oCIS Configurations

Switch context briefly to set up the configuration templates. oCIS includes a quick setup wizard that builds your internal environment and handles self-signed or provided certificate variables.

Bash

# Run initialization tool interactively
su -s /bin/sh ocis -c "ocis init"
  • During initialization, it will prompt you for an admin password. Make sure to write this down.
  • It will ask for the URL. Enter your host domain or internal Incus IP (e.g., https://10.x.x.x:9200).

5. Create the OpenRC Init Service Script

Alpine Linux uses OpenRC instead of systemd. To ensure oCIS runs cleanly as a background daemon when the Incus container boots, build an OpenRC service definition.

Create a new file at /etc/init.d/ocis:

Bash

#!/sbin/openrc-run

description="ownCloud Infinite Scale Service Daemon"
pidfile="/run/${RC_SVCNAME}.pid"
command="/usr/local/bin/ocis"
command_args="server"
command_background="true"
command_user="ocis:ocis"

# Environmental runtime overrides
export OCIS_URL="https://0.0.0.0:9200"
export OCIS_DATA_DIR="/var/lib/ocis/data"
export PROXY_TLS_INSECURE="true" # Use true if terminating TLS via an external host proxy (Nginx/Traefik)

depend() {
    need net
    after firewall
}

start_pre() {
    checkpath -d -m 0750 -o ocis:ocis /var/lib/ocis
}

Make the script executable, add it to Alpine's default boot runlevel, and fire it up:

Bash

chmod +x /etc/init.d/ocis
rc-update add ocis default
rc-service ocis start

Verify it is listening locally inside the container:

Bash

netstat -tulpn

(You should see the oCIS Go microservices listening across internal ports like 9200, 9100, etc.)

6. Expose oCIS to Your LAN/Web Network

To access the cloud interface from your desktop browser, exit the container shell back to your Ubuntu/Incus host and map your incoming traffic.

Option A: Direct Incus Port Forwarding

If you want to route traffic hitting your host machine directly into your Alpine container:

Bash

incus config device add ocis-container proxy9200 proxy listen=tcp:0.0.0.0:9200 connect=tcp:127.0.0.1:9200

Now point your web browser to https://YOUR_HOST_IP:9200 to access ownCloud Infinite Scale.

Option B: Clean Reverse Proxy Configuration (Nginx on Host)

If you want to run a web dashboard cleanly with real SSL certificates, let your host proxy terminate the traffic and forward it internally over the private Incus bridge network:

Nginx

server {
    listen 443 ssl http2;
    server_name cloud.yourdomain.local;

    ssl_certificate /etc/letsencrypt/live/yourdomain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain/privkey.pem;

    location / {
        proxy_pass https://10.x.x.x:9200; # Use your ocis-container Incus IP address
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Vital variables for massive oCIS file transfers over websockets
        client_max_body_size 0;
        proxy_buffering off;
        proxy_read_timeout 3600s;
    }
}

This stack gives you a secure, ultra-lightweight private file system with zero bloated dependencies, operating completely in harmony with the rest of your local Odoo and bot automation workforce tools.

Shared Storage

To understand exactly what is happening when you pass a "specific fast storage directory" into an Incus or Alpine container, you have to look at how Linux handles virtual filesystems.

1. The Standard Container Problem

By default, when you launch a container (like Alpine Linux), all of its files are stored inside a virtual root directory managed by the host. If your host system is running on a standard, slow hard drive or a congested filesystem layout, your ownCloud Infinite Scale (oCIS) database and file transfers will be severely throttled by disk read/write speeds.

2. What is "Fast Storage" Pass-Through?

When we run this command:

Bash

incus config device add ocis-container storage-disk disk source=/mnt/fast-storage path=/var/lib/ocis

We are performing a hardware-level filesystem mount bypass.

You are telling the Linux kernel: "Take this specific physical location (/mnt/fast-storage)—which is physically sitting on a high-speed NVMe SSD, an enterprise ZFS pool, or a dedicated hardware RAID array—and inject it directly into the container at /var/lib/ocis."

3. How the Linux VFS (Virtual File System) Sees This

Linux treats everything as a file, organizing storage targets via a mounting hierarchy.

[ Host Linux VFS Root: / ]
   ├── /etc
   ├── /var
   └── /mnt
        └── /fast-storage  <─── (Physical High-Speed NVMe Drive)
                 │
      ┌──────────┴──────────────┐  (Incus Pass-Through Mount)
      ▼                         ▼
[ Alpine Container Root: / ]    │
   ├── /bin                     │
   ├── /etc                     │
   └── /var/lib/ocis  <─────────┘ (Appends directly here)

Inside the Alpine container, ownCloud thinks it is just writing to a normal local folder (/var/lib/ocis). But in reality, the data completely bypasses the container's virtual disk layer and writes directly to the host's fastest physical silicon at near-zero latency.

Scenario A: Running without "fast-storage" (Pure Native Incus/ZFS)

If you just launch the container normally, Incus automatically provisions a brand new ZFS Dataset (a sub-volume) specifically for that Alpine container.

[ ZFS Storage Pool: nvme_pool ]
   ├── /containers/ocis-container  (Incus automatically manages this dataset)
  • How it works: All of your ownCloud data, application files, and the Alpine OS live together inside one neat, lightning-fast ZFS dataset.
  • The Massive ZFS Benefits: * You get instant, native Incus snapshots (incus snapshot create ocis-container).
    • You get built-in ZFS transparent compression (saves SSD wear and space).
    • You can use incus move to replicate the entire container to another machine instantly.

Scenario B: Running with manual "fast-storage" Pass-Through

You would only use the manual pass-through method on an already-fast system if you want to decouple your application data from the container's lifecycle.

[ ZFS Storage Pool: nvme_pool ]
   ├── /containers/ocis-container  <─── (The Alpine OS only)
   └── /data/my_shared_files       <─── (The actual ownCloud storage passed inside)
  • Why you would do this: * Easy Upgrades: If ownCloud Infinite Scale releases a major update and you want to completely destroy the Alpine container and spin up a fresh one, your data remains completely untouched and safe in /data/my_shared_files.
    • Workforce Sharing: If your Odoo 19 instance or your Python AI bots need to directly read or write files that ownCloud is hosting, it is much easier for them to access a standard host directory like /data/my_shared_files than to try to reach inside an isolated Incus container's private root filesystem.

Summary Checklist

  • Keep it default (No pass-through) if you want the cleanest, most secure isolation and want to rely entirely on Incus for backups and snapshots.
  • Use pass-through if you want your AI bots, Odoo server, and ownCloud instance to easily share the exact same physical folder on your NVMe/ZFS pool.

Storage Pooling and Decoupling

By exploiting the native capabilities of ZFS and Incus, you can create a single, high-speed NVMe storage pool, carve it up into separate datasets (folders with superpower properties), and mount those datasets into multiple completely separate Alpine containers simultaneously.

Here is exactly how that looks and how to set it up.

The Architecture: One Pool, Shared Datasets, Isolated Apps

Instead of letting each container blindly manage its own isolated virtual drive, you orchestrate them from the host level:

                  [ ZFS Pool: nvme_pool ]
                             │
     ┌───────────────────────┼───────────────────────┐
     ▼                       ▼                       ▼
[Dataset: /data/ocis1]  [Dataset: /data/ocis2]  [Dataset: /data/shared_media]
     │                       │                       │
     ▼                       ▼                       ▼
[Alpine Container 1]    [Alpine Container 2]    [Odoo / AI Workforce Bot]
 (ownCloud Team A)       (ownCloud Team B)       (Scapes & Injects Data)

In this setup, Alpine Container 1 and Alpine Container 2 are completely isolated environments (different configurations, different users, different IP addresses), but they are drawing from the exact same physical NVMe speed. Furthermore, you can create a shared_media dataset and mount it into both containers (and your Odoo bot) so they can instantly share assets without network overhead.

Step-by-Step Implementation Blueprint

1. Create the Datasets on the Host (Ubuntu)

On your host terminal, create your target datasets inside your existing ZFS pool (let's assume your pool is named nvme_pool):

Bash

# Create individual storage spaces for different ownCloud instances
zfs create nvme_pool/ocis-storage-1
zfs create nvme_pool/ocis-storage-2

# Create a shared pool for your AI bots and Odoo to drop files into
zfs create nvme_pool/shared-workforce-media

2. Set Permissions Correctly

Because Alpine containers use specific User IDs (UIDs) for security, ensure the host folders allow the container's internal users to read and write to them.

Bash

# If your Alpine container uses UID 100000 for unprivileged mapping:
chown -R 100000:100000 /nvme_pool/ocis-storage-1
chown -R 100000:100000 /nvme_pool/ocis-storage-2
chown -R 100000:100000 /nvme_pool/shared-workforce-media

3. Map Them Using Incus

Now, pass those individual datasets into their respective Alpine containers. You use the Incus disk device option to map the host source path to an internal path.

Bash

# Connect Dataset 1 to Alpine Container 1
incus config device add alpine-ocis-1 target-storage-1 disk source=/nvme_pool/ocis-storage-1 path=/var/lib/ocis

# Connect Dataset 2 to Alpine Container 2
incus config device add alpine-ocis-2 target-storage-2 disk source=/nvme_pool/ocis-storage-2 path=/var/lib/ocis

# Share the Workforce Media dataset with BOTH containers simultaneously
incus config device add alpine-ocis-1 shared-space disk source=/nvme_pool/shared-workforce-media path=/var/lib/ocis/shared
incus config device add alpine-ocis-2 shared-space disk source=/nvme_pool/shared-workforce-media path=/var/lib/ocis/shared

Why this setup is brilliant for your environment:

  • Independent Snapshots: You can snapshot nvme_pool/ocis-storage-1 every hour without touching or bloating the data in ocis-storage-2.
  • Zero-Copy Sharing: Your AI scraping bots can dump a raw file into /nvme_pool/shared-workforce-media on the host, and it will instantly appear inside the filesystems of both Alpine containers without wasting time or CPU cycles uploading via HTTP APIs.

ZFS Quotas per Container: You can limit how much space each ownCloud container can consume so one doesn't accidentally fill up the entire NVMe drive.Bash

zfs set quota=50G nvme_pool/ocis-storage-1

  • Independent Snapshots: You can snapshot nvme_pool/ocis-storage-1 every hour without touching or bloating the data in ocis-storage-2.
  • Zero-Copy Sharing: Your AI scraping bots can dump a raw file into /nvme_pool/shared-workforce-media on the host, and it will instantly appear inside the filesystems of both Alpine containers without wasting time or CPU cycles uploading via HTTP APIs.