How to Install and Configure Nextcloud AIO on Ubuntu 24.04 LTS

This guide walks you through installing and configuring Nextcloud All-in-One (AIO) on an Ubuntu 24.04 LTS (Noble) server, with software RAID, a custom data directory on dedicated storage, and full IPv6 support. It is written from a real Noiz production install (noiz.cloud) running on a dedicated cloud server, and it reflects the specific configuration changes needed to make AIO work reliably on a modern Ubuntu host. The same procedure also works on Ubuntu 22.04 LTS (Jammy), which shares the same networking stack.

Last reviewed: 27 July 2026, against Nextcloud All-in-One (latest stable) on Ubuntu 24.04 LTS. This guide is written for Noiz hosting and documents a real Noiz production deployment. It complements, and does not replace, the official Nextcloud AIO and Docker documentation linked below.

Official Documentation Reference

Hardware Setup Assumptions

The following hardware setup is assumed. Adjust to your own where the specifics differ.

  • NVMe Drives: Two NVMe SSDs in a RAID 1 array, hosting the operating system, swap, and boot partition.
  • HDDs: Four hard disk drives in a RAID 5 array for bulk data storage, providing redundancy with one drive of fault tolerance.
  • Network: A network interface with a routed public IPv6 /64 from your provider.
  • Resources: At least 4 CPU cores and 8 GB RAM. A fully featured AIO install can run up to around 14 containers (a master, five core services, and several optional add-ons), so more resources help.

Prerequisites

  • OS: Ubuntu 24.04 LTS (Noble), 64-bit. The same steps also work on 22.04 LTS (Jammy).
  • Software: Docker Engine from the official Docker APT repository (not the Snap package), mdadm, and basic system tools.
  • Network: Public IPv4 and, ideally, a routed IPv6 /64.
  • Access: Root or sudo on the host.

Step 1: Set Up Software RAID

NVMe RAID 1 is typically pre-configured by the hosting provider. This step covers building the RAID 5 array on the HDDs for Nextcloud data.

1.1 Verify Current Block Devices

lsblk

Confirm the NVMe drives are already part of the existing arrays (/dev/md0 for swap, /dev/md1 for /boot, /dev/md2 for /) and that the HDDs (sda, sdb, sdc, sdd) are unused.

1.2 Create the RAID 5 Array

mdadm --create --verbose /dev/md3 --level=5 --raid-devices=4 /dev/sda /dev/sdb /dev/sdc /dev/sdd

Monitor build progress:

cat /proc/mdstat

Format the array (this may take a long time on large arrays):

mkfs.ext4 /dev/md3

1.3 Mount the Array Persistently

mkdir /data
mount /dev/md3 /data

Get the array UUID:

blkid /dev/md3

Add to /etc/fstab using that UUID (replace with your own):

UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /data ext4 defaults 0 0

Test the fstab entry:

mount -a

1.4 Persist the RAID Configuration

mdadm --detail --scan | grep md3 | tee -a /etc/mdadm/mdadm.conf
update-initramfs -u

This ensures the array is assembled correctly on every boot.

Step 2: Install Docker Engine

Install Docker from the official Docker APT repository. Do not use the Ubuntu Snap package. Its sandboxing causes problems with bind mounts, networks, and AIO's container management.

2.1 Add the Docker APT Repository

apt-get update
apt-get install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update

2.2 Install Docker

apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

2.3 Verify the Install

docker run hello-world

Step 3: Configure Docker daemon.json (DNS + IPv6)

This is the most important step and the one most likely to be done incorrectly. There are two issues to solve at the Docker daemon level.

3.1 Why DNS Configuration Matters

Ubuntu 24.04 (and 22.04 before it) uses systemd-resolved, which puts 127.0.0.53 in the host's /etc/resolv.conf as a stub resolver. When Docker copies this resolv.conf into containers, the loopback address resolves to the container itself rather than the host's resolver, and DNS resolution silently fails inside every container. The AIO master container's first action on startup is a curl to ghcr.io to check for updates, so this DNS failure causes the master to crash-loop indefinitely.

The fix is to explicitly set DNS servers in daemon.json so every container gets working resolvers regardless of what is in the host's resolv.conf.

3.2 Why the Default IPv6 Example Is Wrong

Many guides (including older versions of this one) use 2001:db8:1::/64 as the fixed-cidr-v6 value. This is the RFC 3849 documentation prefix, which is reserved for use in examples and must never appear on a production network. Containers assigned addresses in this range cannot route to the internet, because no router on the internet will accept traffic from or send traffic to this prefix.

The correct approach is to sub-allocate a slice from your provider's routed /64.

3.3 Determine Your IPv6 Sub-Prefix

Check what IPv6 prefix your host has:

ip -6 addr show scope global

Look for the public IPv6 on your main network interface. On a typical dedicated cloud server it often looks like 2a01:4f8:13b:3004::2/64, meaning your provider has routed 2a01:4f8:13b:3004::/64 to the server. This value is an example only; substitute your provider's actual routed prefix.

Pick a /80 sub-prefix from inside this /64 that does not conflict with the host's own address. A safe pattern is to use a discriminator nibble like :1::/80, giving:

2a01:4f8:13b:3004:1::/80

Replace 2a01:4f8:13b:3004 with your own prefix.

3.4 Write the daemon.json

cat > /etc/docker/daemon.json <<'EOF'
{
    "ipv6": true,
    "fixed-cidr-v6": "2a01:4f8:13b:3004:1::/80",
    "ip6tables": true,
    "default-network-opts": {
        "bridge": {
            "com.docker.network.enable_ipv6": "true"
        }
    },
    "dns": ["1.1.1.1", "8.8.8.8"]
}
EOF

3.5 Apply

systemctl restart docker

3.6 Verify IPv6 Works in a Container

docker run --rm alpine sh -c "ip -6 addr show eth0 && ping6 -c 2 ipv6.google.com"

You should see that the container has a public address in your sub-prefix (for example 2a01:4f8:13b:3004:1::3/80) and that ping6 succeeds with reasonable latency.

Step 4: Install Nextcloud AIO

Deploy the AIO master container with the data directory pointed at the RAID 5 array.

4.1 Prepare the Data Directory

AIO expects the data directory to be owned by UID 33 (which is www-data inside the AIO Nextcloud container):

mkdir -p /data/ncdata
chown 33:0 /data/ncdata
chmod 750 /data/ncdata

4.2 Run the Master Container

docker run -d \
  --init \
  --sig-proxy=false \
  --name nextcloud-aio-mastercontainer \
  --restart always \
  --publish 80:80 \
  --publish 8080:8080 \
  --publish 8443:8443 \
  --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
  --volume /var/run/docker.sock:/var/run/docker.sock:ro \
  --env NEXTCLOUD_DATADIR="/data/ncdata" \
  ghcr.io/nextcloud-releases/all-in-one:latest

The --restart always flag ensures the master comes back up automatically on reboot or after a Docker daemon restart.

Note on DNS: Because DNS servers were set in daemon.json in Step 3, the master container inherits working DNS automatically. If you did not complete Step 3, you would need to add --dns 1.1.1.1 --dns 8.8.8.8 to this docker run command to avoid the crash-loop described earlier.

4.3 Confirm the Master Is Healthy

docker ps | grep nextcloud-aio-mastercontainer

Expected: Up X seconds (healthy). If you see Restarting, check the logs with docker logs nextcloud-aio-mastercontainer to diagnose.

Step 5: Configure the Firewall

Open the ports AIO needs, plus your chosen SSH port:

ufw allow 80,443,8080,8443,3478,2222/tcp
ufw allow 3478/udp
ufw reload
ufw enable

Adjust 2222 to whichever non-standard port your SSH service listens on. Ensure IPv6 is also enabled in UFW: check /etc/default/ufw for IPV6=yes.

Port summary:

  • 80: HTTP (used by the AIO Apache container for ACME/Let's Encrypt challenges and HTTP-to-HTTPS redirects).
  • 443: HTTPS (the main Nextcloud entry point, served by the AIO Apache container).
  • 8080: AIO management interface (self-signed cert; see the HSTS note below).
  • 8443: AIO management interface, alternate.
  • 3478/tcp+udp: Nextcloud Talk STUN/TURN.

Step 6: First Access to the AIO Interface

6.1 Access Via Server IP, Not Domain

The AIO management interface uses a self-signed certificate by design. Once Nextcloud is set up and HSTS is enabled on the domain (which it will be), browsers refuse to accept the self-signed cert on port 8080 even though it is a different port. Always access the AIO interface using the server IP, not the domain name:

https://<your-server-ipv4>:8080

For example: https://203.0.113.10:8080 (the address shown here is an example; use your own). Accept the self-signed certificate warning in your browser. Use HTTPS, not HTTP.

6.2 Retrieve the AIO Passphrase

The AIO interface displays a one-time generated passphrase on first run. If you missed it or need to retrieve it later, run the following on the host:

docker exec nextcloud-aio-mastercontainer grep password /mnt/docker-aio-config/data/configuration.json

Save this passphrase in your password manager. It is required for every login to the AIO management interface.

6.3 Complete the Setup Wizard

Log in, enter your Nextcloud domain name, select the optional containers you want (Office, Talk, Whiteboard, and so on), and confirm the data directory shows /data/ncdata.

6.4 Start the Containers

This step is missed easily. After completing the wizard, you must click Start containers in the AIO interface to begin pulling and launching the child containers (Apache, Nextcloud, PostgreSQL, Redis, and any optional add-ons). The initial pull takes 10 to 20 minutes depending on bandwidth, since AIO pulls a dozen or more container images.

Monitor progress in the AIO interface (the indicators turn green as each container becomes healthy) or from the host:

docker ps

Step 7: Post-Installation

7.1 Verify Everything Is Up

docker ps
docker logs nextcloud-aio-mastercontainer
ls -l /data/ncdata

You should see the master container plus its child containers: the five core services (Apache, Nextcloud, PostgreSQL, Redis, and Notify Push) and any optional add-ons you enabled. All should be Up and, eventually, (healthy). The data directory will contain Nextcloud's data files owned by UID 33.

7.2 Enable Backups

Configure the backup target in the AIO interface (a local /mnt/backup path or a remote borg repository). Daily backups are recommended.

7.3 Updates

Future updates of Nextcloud and all child containers are performed via the Stop containers then Start and update containers sequence in the AIO interface. Do not attempt to update child containers directly from the command line, because AIO is the authoritative manager.

7.4 Reboot Persistence

To confirm the install survives a reboot, run reboot on the host and verify that after the host comes back, docker ps shows all containers Up within 1 to 2 minutes. The master comes back via --restart always and starts the children automatically.

Troubleshooting

Master Container Crash-Looping

If docker ps -a | grep mastercontainer shows Restarting:

docker logs --tail 30 nextcloud-aio-mastercontainer

Look for Could not resolve host: ghcr.io, which indicates the DNS problem described in Step 3. Verify /etc/docker/daemon.json contains the "dns" entry and that systemctl restart docker was run after the change.

AIO Interface Unreachable

If https://<server-ip>:8080 times out:

  • Confirm UFW allows port 8080 (ufw status).
  • Confirm any provider-level firewall (for example a cloud firewall in your provider's control panel) also allows port 8080 inbound.
  • Confirm the master container is up and bound to 8080 (docker ps).

HSTS Blocks the AIO Interface

If your browser shows SEC_ERROR_UNKNOWN_ISSUER with no "Accept Risk and Continue" option when accessing https://<your-nextcloud-domain>:8080, this is HSTS blocking. Use the server's IP address instead of the domain name. HSTS is hostname-bound, so the IP is unaffected.

IPv6 Not Working in Containers

Verify the container actually got an address in your sub-prefix:

docker run --rm alpine ip -6 addr

If the container's IPv6 is in a different range than expected, recheck daemon.json and confirm systemctl restart docker was run. If containers have addresses but cannot ping external IPv6 hosts, check that ip6tables: true is set in daemon.json and that the host itself has working outbound IPv6.

Data Directory Permissions

If AIO complains it cannot write to the data directory:

chown 33:0 /data/ncdata
chmod 750 /data/ncdata

UID 33 corresponds to the www-data user inside the AIO Nextcloud container.

Automation Script

The following script automates Steps 3 and 4 (daemon.json configuration and master container launch). It assumes Docker is already installed (Step 2) and the data partition is mounted at /data (Step 1). Edit the IPV6_PREFIX variable to match your own routed IPv6 sub-prefix before running.

#!/bin/bash
set -e

# === Edit this to match your environment ===
IPV6_PREFIX="2a01:4f8:13b:3004:1::/80"
DATA_DIR="/data/ncdata"
# ============================================

echo "Writing /etc/docker/daemon.json..."
cat > /etc/docker/daemon.json <<EOF
{
    "ipv6": true,
    "fixed-cidr-v6": "${IPV6_PREFIX}",
    "ip6tables": true,
    "default-network-opts": {
        "bridge": {
            "com.docker.network.enable_ipv6": "true"
        }
    },
    "dns": ["1.1.1.1", "8.8.8.8"]
}
EOF

echo "Restarting Docker..."
systemctl restart docker

echo "Verifying IPv6 in a test container..."
docker run --rm alpine sh -c "ip -6 addr show eth0 | grep inet6"

echo "Preparing data directory at ${DATA_DIR}..."
mkdir -p "${DATA_DIR}"
chown 33:0 "${DATA_DIR}"
chmod 750 "${DATA_DIR}"

echo "Launching Nextcloud AIO master container..."
docker run -d \
  --init \
  --sig-proxy=false \
  --name nextcloud-aio-mastercontainer \
  --restart always \
  --publish 80:80 \
  --publish 8080:8080 \
  --publish 8443:8443 \
  --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
  --volume /var/run/docker.sock:/var/run/docker.sock:ro \
  --env NEXTCLOUD_DATADIR="${DATA_DIR}" \
  ghcr.io/nextcloud-releases/all-in-one:latest

echo "Done. Access the AIO interface at https://$(hostname -I | awk '{print $1}'):8080"
echo "Retrieve the AIO passphrase with:"
echo "  docker exec nextcloud-aio-mastercontainer grep password /mnt/docker-aio-config/data/configuration.json"

Save as setup_nextcloud_aio.sh, make it executable, and run:

chmod +x setup_nextcloud_aio.sh
./setup_nextcloud_aio.sh

Support

For Noiz hosting customers, open a support ticket via the client area at www.noiz.co.za with the output of docker ps -a, docker logs nextcloud-aio-mastercontainer, and a description of the symptoms.

  • 1 Users Found This Useful
  • nextcloud
Was this answer helpful?

Related Articles

How to Interact with OCC in a Docker Container for Nextcloud Installs

This article explains how to run occ commands in a Nextcloud All-in-One (AIO) Docker container as...