# πŸ“š Documize Community β€” Docker Deployment **Self-hosted knowledge management Β· v5.14.0 Β· MySQL 8 Β· Docker Compose** [![Documize](https://img.shields.io/badge/Documize-v5.14.0-2563EB?style=for-the-badge&logo=gitbook&logoColor=white)](https://github.com/documize/community) [![MySQL](https://img.shields.io/badge/MySQL-8.0-4479A1?style=for-the-badge&logo=mysql&logoColor=white)](https://hub.docker.com/_/mysql) [![Docker](https://img.shields.io/badge/Docker-Compose-2496ED?style=for-the-badge&logo=docker&logoColor=white)](https://docs.docker.com/compose/) [![License](https://img.shields.io/badge/License-GNU_AGPL_v3-22C55E?style=for-the-badge)](https://www.gnu.org/licenses/agpl-3.0.en.html) A production-ready Docker Compose stack for [Documize Community](https://github.com/documize/community) β€” an open-source, self-hosted alternative to Confluence, built with Go + EmberJS. [Quick Start](#-quick-start) Β· [Configuration](#-configuration) Β· [Architecture](#-architecture) Β· [Operations](#-day-to-day-operations) Β· [Troubleshooting](#-troubleshooting)
--- ## ✨ Features - **Single binary deployment** β€” Documize ships as one statically-linked Go binary; no runtime dependencies beyond a database - **Zero reboot-loop risk** β€” Init-container pattern separates the one-time binary download from the always-running app container - **Full-text search** β€” MySQL configured with `ft-min-word-len=3` and `utf8mb4` collation as required by Documize - **Secret-free Compose file** β€” All credentials live in `.env`; `docker-compose.yml` contains no plaintext passwords - **Named volume persistence** β€” Both database data and the app binary survive container restarts and upgrades - **SMTP support** β€” Optional email notifications configured entirely via environment variables --- ## πŸ“‹ Prerequisites | Requirement | Minimum version | |---|---| | Docker Engine | 24.0+ | | Docker Compose | v2.20+ (included with Docker Desktop) | | Available port | TCP `5001` (configurable) | | RAM | 512 MB free (1 GB recommended) | | Disk | 2 GB free | | Internet | Required on first start to download the Documize binary (~25 MB) | --- ## πŸš€ Quick Start ```bash # 1. Clone or download this repository git clone https://github.com/your-org/documize-docker.git cd documize-docker # 2. Create your environment file from the template cp .env.template .env # 3. Generate and set your secrets echo "MYSQL_PASSWORD=$(openssl rand -base64 24)" echo "MYSQL_ROOT_PASSWORD=$(openssl rand -base64 24)" echo "DOCUMIZE_SALT=$(openssl rand -hex 32)" # Paste each value into .env # 4. Protect the file and add to .gitignore chmod 600 .env echo ".env" >> .gitignore # 5. Start the stack docker compose up -d # 6. Watch the logs until Documize is ready (~30–60 s) docker compose logs -f # 7. Open the setup wizard open http://localhost:5001 ``` > **Activation key** β€” The Community edition requires a free activation key. Register your email at [documize.com/community/get-started](https://www.documize.com/community/get-started) to receive one instantly. --- ## πŸ— Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Docker Host β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” exits 0 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ documize-initβ”‚ ──────────► β”‚ app_bin volume β”‚ β”‚ β”‚ β”‚ (alpine:3.19)β”‚ downloads β”‚ /app/bin/documize β”‚ β”‚ β”‚ β”‚ restart: no β”‚ binary once β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ mounts β”‚ β”‚ β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ documize-app (alpine:3.19) β”‚ β”‚ β”‚ β”‚ restart: unless-stopped β”‚ β”‚ β”‚ β”‚ exec /app/bin/documize │◄───┼── :5001 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ TCP 3306 (internal only) β”‚ β”‚ β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ documize-db (mysql:8) β”‚ β”‚ β”‚ β”‚ restart: unless-stopped β”‚ β”‚ β”‚ β”‚ healthcheck: mysqladmin ping β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ db_data β”‚ β”‚ β”‚ β”‚ volume β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Startup Sequence 1. `documize-init` starts β†’ installs `curl` β†’ downloads binary to `app_bin` volume β†’ **exits 0** 2. `documize-db` starts β†’ MySQL initialises β†’ healthcheck passes βœ… 3. `documize-app` starts (only after both conditions above are met) β†’ exec's binary β†’ serves on `:5001` ### Why the Init-Container Pattern? Downloading a binary inside the main container's command creates a reboot loop: any failure causes Docker to restart the container, which retriggers the download. Separating the download into a `restart: "no"` init container means the main app container only ever does one thing β€” run the binary β€” with no network calls or failure modes on restart. --- ## βš™οΈ Configuration ### File Layout ``` documize-docker/ β”œβ”€β”€ docker-compose.yml # Stack definition β€” references ${VARS} from .env β”œβ”€β”€ .env.template # Template β€” copy to .env and fill in secrets β”œβ”€β”€ .env # Your secrets β€” NEVER commit this file └── README.md # This file ``` ### Step 1 β€” Set up your `.env` file Copy the template and fill in all values marked ⚠️: ```bash cp .env.template .env ``` ### Step 2 β€” Environment Variable Reference #### πŸ—„οΈ MySQL Database | Variable | Default | Required | Description | |---|---|---|---| | `MYSQL_DATABASE` | `documize` | Pre-filled | Database name created on first start | | `MYSQL_USER` | `documize` | Pre-filled | MySQL user Documize connects as | | `MYSQL_PASSWORD` | β€” | ⚠️ **Change** | Password for the `documize` MySQL user. Must match the DSN in `DOCUMIZEDB`. Generate: `openssl rand -base64 24` | | `MYSQL_ROOT_PASSWORD` | β€” | ⚠️ **Change** | MySQL root password for admin access. Not used by Documize. Generate: `openssl rand -base64 24` | #### πŸš€ Documize Application | Variable | Default | Required | Description | |---|---|---|---| | `DOCUMIZE_PORT` | `5001` | Pre-filled | Host port Documize is exposed on. Change the left side of the `ports` mapping. | | `DOCUMIZE_SALT` | β€” | ⚠️ **Change** | Password hashing salt. Must be β‰₯ 32 random characters. Generate: `openssl rand -hex 32`. **Set once β€” never change after first run.** | #### πŸ“§ SMTP Email (Optional) All five SMTP variables must be set to enable email. Leave `SMTP_HOST` blank to disable. | Variable | Default | Description | |---|---|---| | `SMTP_HOST` | _(blank)_ | SMTP server hostname (e.g. `smtp.gmail.com`) | | `SMTP_PORT` | `587` | SMTP port β€” `587` for STARTTLS, `465` for SSL/TLS | | `SMTP_USER` | _(blank)_ | SMTP authentication username | | `SMTP_PASSWORD` | _(blank)_ | SMTP authentication password or app-specific password | | `SMTP_SENDER` | `documize@example.com` | From address on outgoing emails | --- ### MySQL Startup Flags These flags are passed to MySQL 8 via the `command` key in `docker-compose.yml` and are **required** for Documize to function correctly. Do not remove them. | Flag | Value | Purpose | |---|---|---| | `--character-set-server` | `utf8mb4` | Full Unicode support including emoji | | `--collation-server` | `utf8mb4_unicode_ci` | Case-insensitive Unicode collation | | `--ft-min-word-len` | `3` | Minimum word length for full-text search index β€” Documize requires exactly `3` | | `--innodb-file-per-table` | `1` | Each table in its own `.ibd` file β€” improves storage reclaim | | `--max-allowed-packet` | `256M` | Maximum packet size for large document imports | --- ### Changing the Host Port Edit the `ports` mapping in `docker-compose.yml` **and** the `DOCUMIZE_PORT` value in `.env`: ```yaml # docker-compose.yml ports: - "${DOCUMIZE_PORT}:5001" # host:container ``` ```bash # .env DOCUMIZE_PORT=8080 ``` --- ### Named Volumes | Volume | Mount path | Purpose | |---|---|---| | `db_data` | `/var/lib/mysql` | All MySQL data β€” content, users, settings. Never delete unless wiping everything. | | `app_bin` | `/app/bin` | The Documize binary. Safe to delete to force a re-download on next start. | --- ## πŸ”’ Security ### Checklist - [ ] `MYSQL_PASSWORD` changed from placeholder - [ ] `MYSQL_ROOT_PASSWORD` changed from placeholder - [ ] `DOCUMIZE_SALT` generated with `openssl rand -hex 32` - [ ] `.env` added to `.gitignore` - [ ] `.env` permissions set to `600` (`chmod 600 .env`) - [ ] MySQL `ports` mapping removed from `docker-compose.yml` (production only) - [ ] Documize placed behind a reverse proxy with TLS (production only) ### Remove the Database Port Mapping (Production) The `db` service does not expose a port by default β€” MySQL is only reachable within the `documize_net` Docker network. If you added a port for local debugging, remove it before deploying: ```yaml # db service β€” remove or comment out for production: # ports: # - "3306:3306" ``` ### Reverse Proxy with TLS Documize serves plain HTTP. In production, terminate TLS at a reverse proxy. Example **Caddy** config: ``` docs.example.com { reverse_proxy localhost:5001 } ``` Example **Nginx** config: ```nginx server { listen 443 ssl; server_name docs.example.com; ssl_certificate /etc/ssl/certs/docs.crt; ssl_certificate_key /etc/ssl/private/docs.key; location / { proxy_pass http://localhost:5001; 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; } } ``` --- ## πŸ“… Day-to-Day Operations ### Start / Stop / Restart ```bash docker compose up -d # start all services (detached) docker compose stop # graceful stop β€” data preserved docker compose start # resume after stop docker compose restart # restart all services docker compose down # stop and remove containers (volumes kept) docker compose down -v # ⚠️ DESTRUCTIVE β€” removes containers AND volumes ``` ### View Logs ```bash docker compose logs -f # all services, follow docker compose logs -f app # Documize app only docker compose logs -f db # MySQL only docker compose logs --tail=100 app # last 100 lines ``` ### Check Status ```bash docker compose ps ``` Expected healthy state: ``` NAME IMAGE STATUS documize-init alpine:3.19 Exited (0) ← correct: one-shot documize-db mysql:8 healthy documize-app alpine:3.19 running ``` ### Open a MySQL Shell ```bash docker exec -it documize-db mysql -u documize -p documize # enter MYSQL_PASSWORD when prompted ``` --- ## πŸ’Ύ Backup & Restore ### Backup the Database ```bash docker exec documize-db \ mysqldump -u documize -p"${MYSQL_PASSWORD}" documize \ > documize-backup-$(date +%Y%m%d-%H%M%S).sql ``` ### Restore the Database ```bash docker exec -i documize-db \ mysql -u documize -p"${MYSQL_PASSWORD}" documize \ < documize-backup-20240101-120000.sql ``` ### Backup the Raw Volume (optional) ```bash docker run --rm \ -v documize_db_data:/data \ -v $(pwd):/backup \ alpine tar czf /backup/db_data-$(date +%Y%m%d).tar.gz -C /data . ``` --- ## ⬆️ Upgrading Documize > ⚠️ **Always back up the database before upgrading.** ```bash # 1. Back up docker exec documize-db mysqldump -u documize -p"${MYSQL_PASSWORD}" documize > pre-upgrade-backup.sql # 2. Stop the stack docker compose down # 3. Remove the cached binary to force re-download of the new version docker volume rm documize_app_bin # 4. Update the download URL in docker-compose.yml (app-init command) to the new version # 5. Start and watch for successful migration messages docker compose up -d docker compose logs -f app ``` Documize runs database schema migrations automatically on startup. --- ## πŸ› Troubleshooting | Symptom | Cause | Resolution | |---|---|---| | `app-init` exits with code `127` | `curl` not found β€” `apk` failed | Check internet connectivity and retry: `docker compose down -v && docker compose up -d` | | `app-init` exits with code `22` or `28` | Binary download failed | Check outbound HTTPS access to `community-downloads.s3.us-east-2.amazonaws.com` | | `documize-app` keeps restarting | MySQL not healthy or DSN mismatch | Run `docker compose logs db`. Verify `MYSQL_PASSWORD` in `.env` matches the password in `DOCUMIZEDB` | | Setup wizard shows DB connection error | DSN credentials mismatch | Ensure `MYSQL_PASSWORD` in `.env` is identical to the password in the `DOCUMIZEDB` connection string | | Port `5001` already in use | Port conflict on host | Change `DOCUMIZE_PORT` in `.env` and restart | | `app-init` exits with code `0` but binary won't run | Wrong CPU architecture | The default binary is `linux-amd64`. On ARM (e.g. Raspberry Pi, Apple Silicon Linux), replace the download URL with the `linux-arm64` binary | | Full-text search returns no results | MySQL FTS config missing | Confirm `--ft-min-word-len=3` is present in the `db` `command` block | | Lost `DOCUMIZE_SALT` β€” users locked out | Salt cannot be recovered | Restore from a database backup taken before the salt was changed | | Docker Compose variable not substituted | `.env` file missing or wrong path | Ensure `.env` is in the **same directory** as `docker-compose.yml` | --- ## πŸ“ File Reference ``` . β”œβ”€β”€ docker-compose.yml # Stack definition β€” edit to change ports and resource limits β”œβ”€β”€ .env.template # Commit this β€” safe template with no real secrets β”œβ”€β”€ .env # Do NOT commit β€” your actual secrets └── README.md # This file ``` | Docker resource | Type | Purpose | |---|---|---| | `documize-db` | Container | MySQL 8 database | | `documize-init` | Container | One-shot binary downloader | | `documize-app` | Container | Documize application | | `documize_net` | Network | Private bridge β€” only `app` can reach `db` | | `db_data` | Volume | MySQL data directory | | `app_bin` | Volume | Documize binary cache | --- ## πŸ†˜ Quick-Reference Commands ```bash # ── Setup ────────────────────────────────────────────────────────────────────── cp .env.template .env && chmod 600 .env # create secrets file openssl rand -hex 32 # generate DOCUMIZE_SALT openssl rand -base64 24 # generate a password # ── Stack lifecycle ──────────────────────────────────────────────────────────── docker compose up -d # start docker compose down # stop (data kept) docker compose down -v # ⚠️ wipe everything docker compose logs -f # live logs docker compose ps # status # ── Database ─────────────────────────────────────────────────────────────────── docker exec -it documize-db mysql -u documize -p documize # MySQL shell docker exec documize-db mysqldump -u documize -p"" documize > backup.sql # ── Upgrades ─────────────────────────────────────────────────────────────────── docker compose down && docker volume rm documize_app_bin && docker compose up -d ``` --- ## πŸ“„ License Documize Community edition is licensed under the **GNU Affero General Public License v3 (AGPL-3.0)**. See [LICENSE](https://github.com/documize/community/blob/master/LICENSE) in the upstream repository. This Docker configuration is provided as-is for self-hosting purposes. ---
Made with ❀️ for the self-hosting community · [Documize upstream](https://github.com/documize/community) · [Report an issue](https://github.com/your-org/documize-docker/issues)