444 lines
18 KiB
Markdown
444 lines
18 KiB
Markdown
<div align="center">
|
||||
|
|
|
|||
|
|
# 📚 Documize Community — Docker Deployment
|
|||
|
|
|
|||
|
|
**Self-hosted knowledge management · v5.14.0 · MySQL 8 · Docker Compose**
|
|||
|
|
|
|||
|
|
[](https://github.com/documize/community)
|
|||
|
|
[](https://hub.docker.com/_/mysql)
|
|||
|
|
[](https://docs.docker.com/compose/)
|
|||
|
|
[](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)
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✨ 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"<pw>" 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.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
<div align="center">
|
|||
|
|
|
|||
|
|
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)
|
|||
|
|
|
|||
|
|
</div>
|