Nextcloud + Onlyoffice deploy by using yml

Gerard Chen Avatar

最近一段時間密集使用 Docker 佈署各種應用服務,一開始其實沒有特別留意系統資源的使用狀況。直到某一天,突然發現主機前方的系統運作指示燈幾乎沒有熄滅過,心裡只覺得怪怪的。打開系統監控工具一看,才發現事情大條了 —— RAM 幾乎被吃光。

檢查後發現,原來「資源消耗怪獸」是:

Nextcloud + OnlyOffice(Community Server + Document Server + Elasticsearch)+ MySQL

光是完成佈署、系統閒置、甚至還沒做任何壓力測試,記憶體使用量就已經輕鬆突破 10GB

「好吧,那就買 RAM 來改善把~~~」

一查價格,我的老天鵝啊~~~~~ 今年 2 月才剛組了一台新電腦,同樣品牌、同樣型號。 32GB RAM,當時大約 NT$2,800。現在再查看一次價格 —— 接近3倍旳 NT$9,900。

回想幾個星期前,曾在某個平台(一時想不起來是哪裡)聽到有人提到:

由於 AI 需求動能強勁記憶體(RAM)價格正在快速上漲,且漲勢仍在持續。

當時心裡還想:「能漲到什麼程度?」
現在才發現自己真的是天真了。

目前先將佈署流程與設定完整記錄下來,才不會以後忘記佈署的細節。接下來打算利用現有的 2 台電腦,練習如何將不同的 service 佈署在不同的 server 上協同運作,分散 RAM 與 I/O 的資源消耗。

Bash Script × 2, dirCREATOR / initSQL
Docker Compose(yml)× 1
.env x1

dirCREATOR
負責根據 docker-compose.yml 中定義的 volume 結構,自動建立所有必要的資料夾與目錄階層。

initSQL
剛開始佈署,在這卡了很久。 Docker Compose 初始啟動時,SQL 初始化存在 bug,如果沒有先將SQL初始化搞定,會一直卡關。

佈署流程

dirCREATOR

initSQL

docker compose up -d


dirCREATOR

#!/usr/bin/env bash

# Base data directory relative to the script location
BASE_DIR="./data"

echo "Creating directory structure for Nextcloud + OnlyOffice ( community + document + elacticsearch ) + MySQL"

# Format: "path:owner_uid:group_gid:permissions"
DIRS=(
    "$BASE_DIR/nextcloud_data:33:33:750"
    "$BASE_DIR/mysql/conf.d:0:0:755"
    "$BASE_DIR/mysql/docker-entrypoint-initdb.d:0:0:755"
    "$BASE_DIR/mysql_data:999:999:750"
    "$BASE_DIR/community_data:0:0:755"
    "$BASE_DIR/community_log:0:0:755"
    "$BASE_DIR/community_letsencrypt:0:0:755"
    "$BASE_DIR/document_data:0:0:755"
    "$BASE_DIR/document_log:0:0:755"
    "$BASE_DIR/document_forgotten:0:0:755"
    "$BASE_DIR/document_fonts:0:0:755"
    "$BASE_DIR/certs:0:0:755"
    "$BASE_DIR/es_data:1000:1000:775"
    "$BASE_DIR/mail_data:0:0:755"
    "$BASE_DIR/mail_certs:0:0:755"
    "$BASE_DIR/mail_log:0:0:755"
    "$BASE_DIR/controlpanel_data:0:0:755"
    "$BASE_DIR/controlpanel_log:0:0:755"
)

if [ "$EUID" -ne 0 ]; then
    echo "Please run as root (use sudo)"
    exit 1
fi

for entry in "${DIRS[@]}"; do
    IFS=':' read -r path uid gid perm <<< "$entry"
    echo "Processing: $path"
    mkdir -p "$path"
    chown "$uid:$gid" "$path"
    chmod "$perm" "$path"
done

echo "Directory setup complete."

initSQL

#!/usr/bin/env bash
# --- ONLYOFFICE & NEXTCLOUD MySQL Configuration Creator ---
set -e

# --- 1. Define Paths and Credentials ---
BASE_DIR="./data/mysql"
CONF_DIR="${BASE_DIR}/conf.d"
INIT_DB_DIR="${BASE_DIR}/docker-entrypoint-initdb.d"
CONF_FILE="${CONF_DIR}/onlyoffice.cnf"
INIT_SQL_FILE="${INIT_DB_DIR}/init.sql"

# Credentials
OO_USER="onlyoffice"
OO_PASS="adjust to your pass"
MAIL_USER="onlyoffice_mail"
MAIL_PASS="adjust to your pass"

# Nextcloud Credentials
NC_DB="nextcloud"
NC_USER="onlyoffice"
NC_PASS="adjust to your pass"

# --- 2. Ensure Directories Exist ---
echo "Ensuring configuration directories exist..."
mkdir -p "$CONF_DIR"
mkdir -p "$INIT_DB_DIR"

# --- 3. Write MySQL Configuration ---
echo "Writing MySQL configuration to ${CONF_FILE}..."
cat << 'EOF' > "$CONF_FILE"
[mysqld]
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
max_allowed_packet=256M
innodb_buffer_pool_size=1G
innodb_log_file_size=256M
innodb_flush_log_at_trx_commit=2
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
binlog_format=ROW
EOF

# --- 4. Write Database Initialization Script ---
echo "Writing database user initialization script to ${INIT_SQL_FILE}..."
cat << EOF > "$INIT_SQL_FILE"
-- 1. Create Databases
CREATE DATABASE IF NOT EXISTS onlyoffice CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS onlyoffice_mailserver CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS ${NC_DB} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 2. Create Users
CREATE USER IF NOT EXISTS '${OO_USER}'@'%' IDENTIFIED WITH mysql_native_password BY '${OO_PASS}';
CREATE USER IF NOT EXISTS '${MAIL_USER}'@'%' IDENTIFIED WITH mysql_native_password BY '${MAIL_PASS}';

-- 3. Grant Privileges
GRANT ALL PRIVILEGES ON onlyoffice.* TO '${OO_USER}'@'%';
GRANT ALL PRIVILEGES ON onlyoffice_mailserver.* TO '${MAIL_USER}'@'%';
GRANT ALL PRIVILEGES ON ${NC_DB}.* TO '${OO_USER}'@'%';

-- 4. Finalize
FLUSH PRIVILEGES;
-- Set to Taipei time (+08:00)
SET GLOBAL time_zone = '+08:00';
EOF

echo "Initial scripts created successfully."

.env

# -----------------------------
# SYSTEM
# -----------------------------
TZ=Asia/Taipei
MYSQL_ROOT_PASSWORD="adjust to your pass"

# -----------------------------
# DATABASE
# -----------------------------
MYSQL_SERVER_HOST=onlyoffice-mysql-server
MYSQL_SERVER_USER=onlyoffice
MYSQL_SERVER_PASS="adjust to your pass"
MYSQL_SERVER_DB_NAME=onlyoffice

# -----------------------------
# MAIL
# -----------------------------
MAIL_SERVER_HOSTNAME="adjust to your hostname"
MAIL_DB_NAME=onlyoffice_mailserver
MAIL_DB_USER=onlyoffice_mail
MAIL_DB_PASS="adjust to your pass"

# -----------------------------
# SMTP RELAY
# -----------------------------
MAIL_RELAY_HOST="adjust to your hostname"
MAIL_RELAY_PORT=587
MAIL_RELAY_USER="adjust to your email"
MAIL_RELAY_PASSWD="adjust to your pass"


# -----------------------------
# SECURITY (UNIQUE FOR GERARDCHEN INSTANCE)
# -----------------------------
JWT_SECRET="adjust to your secret"

DOCUMENT_SERVER_JWT_HEADER=AuthorizationJwt
ONLYOFFICE_CORE_MACHINEKEY="adjust to your key"

# -----------------------------
# NEXTCLOUD
# -----------------------------
NEXTCLOUD_DB_PASSWORD="adjust to your pass"

# -----------------------------
# DOMAINS
# -----------------------------
CLOUD_DOMAIN="adjust to your url"
OFFICE_DOMAIN="adjust to your url"
DOCUMENT_DOMAIN="adjust to your url"

docker-compose.yml

services:
# --- REDIS ---
redis:
image: redis:alpine
container_name: onlyoffice-redis
restart: always
networks:
- onlyoffice

# --- MYSQL SERVER ---
mysql-server:
container_name: onlyoffice-mysql-server
image: mysql:8.0.29
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- TZ=${TZ}
networks:
- onlyoffice
restart: always
volumes:
- ./data/mysql/conf.d:/etc/mysql/conf.d
- ./data/mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
- ./data/mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s

# --- NEXTCLOUD ---
nextcloud:
image: nextcloud:latest
container_name: nextcloud
restart: always
depends_on:
mysql-server:
condition: service_healthy
redis:
condition: service_started
ports:
- "port:80"
environment:
- MYSQL_HOST=onlyoffice-mysql-server
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=${MYSQL_SERVER_USER}
- MYSQL_PASSWORD=${MYSQL_SERVER_PASS}
- NEXTCLOUD_TRUSTED_DOMAINS=${CLOUD_DOMAIN} domain.local 127.0.0.1
- REDIS_HOST=redis
- OVERWRITEPROTOCOL=https
- OVERWRITEHOST=${CLOUD_DOMAIN}
- TZ=${TZ}
volumes:
- ./data/nextcloud_data:/var/www/html
- /mnt/path to your physical harddisk:/path to your physical harddisk
- /mnt/path to your physical harddisk:/mnt/path to your physical harddisk
- /mnt/path to your physical harddisk:/mnt/path to your physical harddisk
- /mnt/path to your physical harddisk:/mnt/path to your physical harddisk:
- onlyoffice

# --- DOCUMENT SERVER ---
onlyoffice-document-server:
container_name: onlyoffice-document-server
image: onlyoffice/documentserver:8.1
restart: always
environment:
- JWT_ENABLED=true
- JWT_SECRET=${JWT_SECRET}
- JWT_HEADER=${DOCUMENT_SERVER_JWT_HEADER}
- TZ=${TZ}
- REDIS_SERVER_HOST=redis
networks:
- onlyoffice
ports:
- 'port:80'
volumes:
- ./data/document_data:/var/www/onlyoffice/Data
- ./data/document_log:/var/log/office
- ./data/document_fonts:/usr/share/fonts/truetype/custom
- ./data/document_forgotten:/var/lib/onlyoffice/documentserver/App_Data/cache/files/forgotten

# --- ONLYOFFICE COMMUNITY SERVER ---
onlyoffice-community-server:
container_name: onlyoffice-community-server
image: onlyoffice/communityserver:12.6.0.1900
depends_on:
mysql-server:
condition: service_healthy
onlyoffice-document-server:
condition: service_started
redis:
condition: service_started
environment:
- ONLYOFFICE_CORE_MACHINEKEY=${ONLYOFFICE_CORE_MACHINEKEY}
- TZ=${TZ}
- REDIS_SERVER_HOST=redis
- DOCUMENT_SERVER_PORT_80_TCP_ADDR=onlyoffice-document-server
- DOCUMENT_SERVER_PORT_80_TCP_PROTO=http
- DOCUMENT_SERVER_URL_PUBLIC=https://${DOCUMENT_DOMAIN}/
- DOCUMENT_SERVER_URL_INTERNAL=http://onlyoffice-document-server/
- DOCUMENT_SERVER_JWT_ENABLED=true
- DOCUMENT_SERVER_JWT_SECRET=${JWT_SECRET}
- DOCUMENT_SERVER_JWT_HEADER=${DOCUMENT_SERVER_JWT_HEADER}
- MYSQL_SERVER_HOST=onlyoffice-mysql-server
- MYSQL_SERVER_DB_NAME=${MYSQL_SERVER_DB_NAME}
- MYSQL_SERVER_USER=${MYSQL_SERVER_USER}
- MYSQL_SERVER_PASS=${MYSQL_SERVER_PASS}
networks:
- onlyoffice
ports:
- 'port:80'
privileged: true
cgroup: host
restart: always
volumes:
- ./data/community_data:/var/www/onlyoffice/Data
- ./data/community_log:/var/log/onlyoffice
- ./data/community_ds_data:/var/www/onlyoffice/DocumentServerData
- /sys/fs/cgroup:/sys/fs/cgroup:rw
- ./data/certs:/var/www/onlyoffice/Data/certs

# --- ELASTICSEARCH ---
onlyoffice-elasticsearch:
image: onlyoffice/elasticsearch:7.16.3
container_name: onlyoffice-elasticsearch
restart: always
environment:
- discovery.type=single-node
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms1g -Xmx1g"
networks:
- onlyoffice
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./data/es_data:/usr/share/elasticsearch/data

# --- MAIL SERVER ---
onlyoffice-mail-server:
container_name: onlyoffice-mail-server
image: onlyoffice/mailserver:1.6.75
depends_on:
mysql-server:
condition: service_healthy
hostname: ${MAIL_SERVER_HOSTNAME}
environment:
- MYSQL_SERVER=onlyoffice-mysql-server
- MYSQL_SERVER_PORT=3306
- MYSQL_ROOT_USER=${MAIL_DB_USER}
- MYSQL_ROOT_PASSWD=${MAIL_DB_PASS}
- MYSQL_SERVER_DB_NAME=${MAIL_DB_NAME}
- TZ=${TZ}
- MAIL_RELAY_HOST=${MAIL_RELAY_HOST}
- MAIL_RELAY_PORT=${MAIL_RELAY_PORT}
- MAIL_RELAY_USER=${MAIL_RELAY_USER}
- MAIL_RELAY_PASSWD=${MAIL_RELAY_PASSWD}
networks:
- onlyoffice
restart: always
privileged: true
volumes:
- ./data/mail_data:/var/vmail
- ./data/mail_certs:/etc/pki/tls/mailserver
- ./data/mail_log:/var/log

# --- CONTROL PANEL ---
onlyoffice-control-panel:
container_name: onlyoffice-control-panel
image: onlyoffice/controlpanel:3.5.2.530
depends_on:
- onlyoffice-document-server
- onlyoffice-mail-server
- onlyoffice-community-server
environment:
- ONLYOFFICE_CORE_MACHINEKEY=${ONLYOFFICE_CORE_MACHINEKEY}
- TZ=${TZ}
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data/controlpanel_data:/var/www/onlyoffice/Data
- ./data/controlpanel_log:/var/log/onlyoffice
networks:
- onlyoffice

networks:
onlyoffice:
driver: 'bridge'

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *