#!/bin/bash # ONLYOFFICE DocumentServer Startup Script # This script initializes and starts all required services umask 0022 # ============================================ # HARDCODED CONFIGURATION # ============================================ COMPANY_NAME=${COMPANY_NAME:-onlyoffice} PRODUCT_NAME=${PRODUCT_NAME:-documentserver} # Database Configuration DB_TYPE=${DB_TYPE:-postgres} DB_HOST=${DB_HOST:-localhost} DB_PORT=${DB_PORT:-5432} DB_NAME=${DB_NAME:-onlyoffice} DB_USER=${DB_USER:-onlyoffice} DB_PWD=${DB_PWD:-onlyoffice} # Redis Configuration REDIS_SERVER_HOST=${REDIS_SERVER_HOST:-localhost} REDIS_SERVER_PORT=${REDIS_SERVER_PORT:-6379} REDIS_ENABLED=${REDIS_ENABLED:-true} # RabbitMQ Configuration AMQP_SERVER_HOST=${AMQP_SERVER_HOST:-localhost} AMQP_SERVER_PORT=${AMQP_SERVER_PORT:-5672} AMQP_SERVER_USER=${AMQP_SERVER_USER:-guest} AMQP_SERVER_PWD=${AMQP_SERVER_PWD:-guest} AMQP_SERVER_PROTO=${AMQP_SERVER_PROTO:-amqp} # JWT Configuration JWT_ENABLED=${JWT_ENABLED:-false} JWT_SECRET=${JWT_SECRET:-secret} JWT_HEADER=${JWT_HEADER:-Authorization} JWT_IN_BODY=${JWT_IN_BODY:-false} # Other settings WOPI_ENABLED=${WOPI_ENABLED:-false} GENERATE_FONTS=${GENERATE_FONTS:-true} PLUGINS_ENABLED=${PLUGINS_ENABLED:-true} DS_LOG_LEVEL=${DS_LOG_LEVEL:-WARN} NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-1} # ============================================ # DIRECTORIES # ============================================ APP_DIR="/var/www/${COMPANY_NAME}/documentserver" DATA_DIR="/var/www/${COMPANY_NAME}/Data" PRIVATE_DATA_DIR="${DATA_DIR}/.private" LOG_DIR="/var/log/${COMPANY_NAME}" DS_LOG_DIR="${LOG_DIR}/documentserver" LIB_DIR="/var/lib/${COMPANY_NAME}" DS_LIB_DIR="${LIB_DIR}/documentserver" CONF_DIR="/etc/${COMPANY_NAME}/documentserver" PG_ROOT="/var/lib/postgresql" PGDATA="${PG_ROOT}/${PG_VERSION:-16}/main" REDIS_DATA="/var/lib/redis" RABBITMQ_DATA="/var/lib/rabbitmq" # ============================================ # FUNCTIONS # ============================================ start_process() { "$@" & CHILD=$! wait "$CHILD" CHILD="" } clean_exit() { [[ -z "$CHILD" ]] || kill -s SIGTERM "$CHILD" 2>/dev/null /usr/bin/documentserver-prepare4shutdown.sh 2>/dev/null || true exit } trap clean_exit SIGTERM SIGQUIT SIGABRT SIGINT waiting_for_db() { echo "Waiting for database connection..." local RETRY=0 local MAX_RETRY=30 while ! PGPASSWORD="${DB_PWD}" psql -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" -c "SELECT 1" >/dev/null 2>&1; do RETRY=$((RETRY + 1)) if [ $RETRY -ge $MAX_RETRY ]; then echo "ERROR: Database connection failed after ${MAX_RETRY} attempts" exit 1 fi echo " Attempt ${RETRY}/${MAX_RETRY}..." sleep 2 done echo "Database connection established!" } waiting_for_amqp() { echo "Waiting for RabbitMQ connection..." local RETRY=0 local MAX_RETRY=30 while ! nc -z "${AMQP_SERVER_HOST}" "${AMQP_SERVER_PORT}" 2>/dev/null; do RETRY=$((RETRY + 1)) if [ $RETRY -ge $MAX_RETRY ]; then echo "ERROR: RabbitMQ connection failed after ${MAX_RETRY} attempts" exit 1 fi echo " Attempt ${RETRY}/${MAX_RETRY}..." sleep 2 done echo "RabbitMQ connection established!" } waiting_for_redis() { echo "Waiting for Redis connection..." local RETRY=0 local MAX_RETRY=30 while ! nc -z "${REDIS_SERVER_HOST}" "${REDIS_SERVER_PORT}" 2>/dev/null; do RETRY=$((RETRY + 1)) if [ $RETRY -ge $MAX_RETRY ]; then echo "ERROR: Redis connection failed after ${MAX_RETRY} attempts" exit 1 fi echo " Attempt ${RETRY}/${MAX_RETRY}..." sleep 2 done echo "Redis connection established!" } create_postgresql_cluster() { echo "Creating PostgreSQL cluster..." sudo -u postgres /usr/lib/postgresql/${PG_VERSION:-16}/bin/initdb -D "${PGDATA}" # Configure pg_hba.conf to trust local connections # This avoids password authentication issues between services running in the same container echo "local all onlyoffice trust" > "${PGDATA}/pg_hba.conf" echo "local all all peer" >> "${PGDATA}/pg_hba.conf" echo "host all all 127.0.0.1/32 trust" >> "${PGDATA}/pg_hba.conf" echo "host all all ::1/128 trust" >> "${PGDATA}/pg_hba.conf" } create_postgresql_db() { echo "Creating PostgreSQL database..." sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH password '${DB_PWD}';" 2>/dev/null || true sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" 2>/dev/null || true } create_postgresql_tbl() { echo "Creating database tables..." PGPASSWORD="${DB_PWD}" psql -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" \ -f "${APP_DIR}/server/schema/postgresql/createdb.sql" 2>/dev/null || true } update_ds_settings() { echo "Updating DocumentServer settings..." local JSON="${CONF_DIR}/local.json" # Create local.json with all settings cat > "${JSON}" << EOF { "services": { "CoAuthoring": { "sql": { "type": "${DB_TYPE}", "dbHost": "${DB_HOST}", "dbPort": "${DB_PORT}", "dbName": "${DB_NAME}", "dbUser": "${DB_USER}", "dbPass": "${DB_PWD}" }, "redis": { "host": "${REDIS_SERVER_HOST}", "port": ${REDIS_SERVER_PORT} }, "pubsub": { "maxNumberOfReceivers": "infinity" }, "token": { "enable": { "request": { "inbox": ${JWT_ENABLED}, "outbox": ${JWT_ENABLED} }, "browser": ${JWT_ENABLED} }, "inbox": { "header": "${JWT_HEADER}", "inBody": ${JWT_IN_BODY} }, "outbox": { "header": "${JWT_HEADER}", "inBody": ${JWT_IN_BODY} } }, "secret": { "inbox": { "string": "${JWT_SECRET}" }, "outbox": { "string": "${JWT_SECRET}" }, "session": { "string": "${JWT_SECRET}" } } } }, "rabbitmq": { "url": "${AMQP_SERVER_PROTO}://${AMQP_SERVER_USER}:${AMQP_SERVER_PWD}@${AMQP_SERVER_HOST}:${AMQP_SERVER_PORT}" }, "wopi": { "enable": ${WOPI_ENABLED} } } EOF chown ds:ds "${JSON}" chmod 644 "${JSON}" } # ============================================ # MAIN STARTUP SEQUENCE # ============================================ echo "============================================" echo "ONLYOFFICE DocumentServer Startup" echo "============================================" echo "Database: ${DB_HOST}:${DB_PORT}/${DB_NAME}" echo "Redis: ${REDIS_SERVER_HOST}:${REDIS_SERVER_PORT}" echo "RabbitMQ: ${AMQP_SERVER_HOST}:${AMQP_SERVER_PORT}" echo "JWT Enabled: ${JWT_ENABLED}" echo "============================================" # Create log directories for i in converter docservice metrics adminpanel; do mkdir -p "$DS_LOG_DIR/$i" && touch "$DS_LOG_DIR/$i"/{out,err}.log done mkdir -p "${DS_LOG_DIR}-example" && touch "${DS_LOG_DIR}-example"/{out,err}.log # Create app directories for i in ${DS_LIB_DIR}/App_Data/cache/files ${DS_LIB_DIR}/App_Data/docbuilder ${DS_LIB_DIR}-example/files; do mkdir -p "$i" done # Create data directory mkdir -p "${DATA_DIR}" mkdir -p "${PRIVATE_DATA_DIR}" # Set permissions chown ds:ds "${DATA_DIR}" for i in ${DS_LOG_DIR} ${DS_LOG_DIR}-example ${LIB_DIR}; do chown -R ds:ds "$i" chmod -R 755 "$i" done # Array to track local services that need to be started LOCAL_SERVICES=() # Handle database if [ "${DB_HOST}" = "localhost" ]; then echo "Starting local PostgreSQL..." chown -R postgres:postgres ${PG_ROOT} chmod -R 700 ${PG_ROOT} if [ ! -d "${PGDATA}" ]; then create_postgresql_cluster PG_NEW_CLUSTER=true fi LOCAL_SERVICES+=("postgresql") fi # Handle RabbitMQ if [ "${AMQP_SERVER_HOST}" = "localhost" ]; then echo "Starting local RabbitMQ..." chown -R rabbitmq:rabbitmq ${RABBITMQ_DATA} chmod -R go=rX,u=rwX ${RABBITMQ_DATA} if [ -f ${RABBITMQ_DATA}/.erlang.cookie ]; then chmod 400 ${RABBITMQ_DATA}/.erlang.cookie fi rm -rf /var/run/rabbitmq LOCAL_SERVICES+=("rabbitmq-server") fi # Handle Redis if [ "${REDIS_ENABLED}" = "true" ] && [ "${REDIS_SERVER_HOST}" = "localhost" ]; then echo "Starting local Redis..." chown -R redis:redis ${REDIS_DATA} chmod -R 750 ${REDIS_DATA} LOCAL_SERVICES+=("redis-server") fi # Start local services for i in ${LOCAL_SERVICES[@]}; do echo "Starting service: $i" service $i start done # Wait for and initialize database if [ "${DB_HOST}" = "localhost" ]; then sleep 3 if [ "${PG_NEW_CLUSTER}" = "true" ]; then create_postgresql_db create_postgresql_tbl fi else waiting_for_db # Check if tables exist, if not create them PGPASSWORD="${DB_PWD}" psql -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" \ -c "SELECT 1 FROM doc_changes LIMIT 1" 2>/dev/null || create_postgresql_tbl fi # Wait for other services waiting_for_amqp if [ "${REDIS_ENABLED}" = "true" ]; then waiting_for_redis fi # Update DocumentServer configuration update_ds_settings # Configure nginx echo "Configuring Nginx..." NGINX_ONLYOFFICE_PATH="${CONF_DIR}/nginx" NGINX_ONLYOFFICE_CONF="${NGINX_ONLYOFFICE_PATH}/ds.conf" ln -sf ${NGINX_ONLYOFFICE_PATH}/ds.conf.tmpl ${NGINX_ONLYOFFICE_CONF} # Install plugins if enabled if [ "${PLUGINS_ENABLED}" = "true" ]; then echo "Installing plugins..." start_process documentserver-pluginsmanager.sh -r false --update=\"${APP_DIR}/sdkjs-plugins/plugin-list-default.json\" >/dev/null 2>&1 || true echo "Plugins installed." fi # Configure ds-example if enabled if [ "${DS_EXAMPLE}" = "true" ]; then echo "Enabling test example service..." cat > /etc/supervisor/conf.d/ds-example.conf </dev/null || true # Start nginx echo "Starting Nginx..." service nginx start # Generate fonts if enabled if [ "${GENERATE_FONTS}" = "true" ]; then echo "Generating fonts..." start_process documentserver-generate-allfonts.sh false >/dev/null 2>&1 || true echo "Fonts generated." fi # Compress static files start_process documentserver-static-gzip.sh false >/dev/null 2>&1 || true echo "============================================" echo "ONLYOFFICE DocumentServer is ready!" echo "Access at: http://localhost" if [ "${JWT_ENABLED}" = "true" ]; then echo "JWT Secret: ${JWT_SECRET}" fi echo "============================================" # Keep container running and tail logs exec tail -F ${DS_LOG_DIR}/docservice/out.log ${DS_LOG_DIR}/converter/out.log 2>/dev/null