# Setting up Docs
Created: [[2025_08_28]] 19:10
Tags: #selfhost #Docker #Software
I had trouble setting up the [La Suite Docs](https://github.com/suitenumerique/docs) project, so I figured that it would be nice to share my setup for this.
![[la_suite_docs_screenshot.png]]
Here are some pre-requisites:
- Docker Compose
- Existing OIDC infra (I use [[Pocket-ID]] which is fantastically simple)
- Emailing system (I use Fastmail)
- Domain name that you can attach sub-domains to
- Reverse proxy (I use caddy and have a Caddyfile below)
The system was built for the French Government, so it obviously is set up in production with a much more complicated Kubernetes setup (which you can read more about [how they set it up here](https://github.com/suitenumerique/docs/tree/main/docs/installation)). For self-hosting, this may be cumbersome for people like me, who prefer to do this as a hobby rather than as a 2nd job.
Here is how the setup works:
![[Setting up Docs 2025_08_28-22.21.excalidraw.svg]]
Here is the whole compose file:
```yaml
services:
# DB
postgresql:
image: postgres:16
container_name: docs_postgres
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -d ${POSTGRES_DB} -U ${POSTGRES_USER}"]
interval: 1s
timeout: 2s
retries: 300
env_file:
- stack.env
environment:
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
- docs_db:/var/lib/postgresql/data/pgdata
# RESP compatible server
redis:
container_name: docs_redis
image: valkey/valkey:latest
restart: unless-stopped
# Docs backend
backend:
image: lasuite/impress-backend:latest
container_name: docs_backend
user: ${DOCKER_USER:-1000}
restart: unless-stopped
environment:
- DJANGO_CONFIGURATION=Production
env_file:
- stack.env
healthcheck:
test: ["CMD", "python", "manage.py", "check"]
interval: 15s
timeout: 30s
retries: 20
start_period: 10s
depends_on:
postgresql:
condition: service_healthy
restart: true
redis:
condition: service_started
# Docs realtime collab
y-provider:
image: lasuite/impress-y-provider:latest
container_name: docs_collab
user: ${DOCKER_USER:-1000}
restart: unless-stopped
env_file:
- stack.env
# Docs frontend app
frontend:
image: lasuite/impress-frontend:latest
container_name: docs_frontend
restart: unless-stopped
user: "101"
entrypoint:
- /docker-entrypoint.sh
command: ["nginx", "-g", "daemon off;"]
env_file:
- stack.env
volumes:
- frontend-nginx:/etc/nginx/templates/docs.conf.template
depends_on:
backend:
condition: service_healthy
# S3 compatible storage
minio:
image: minio/minio
container_name: docs_minio
restart: unless-stopped
env_file:
- stack.env
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 1s
timeout: 20s
retries: 300
entrypoint: ""
command: minio server /data
volumes:
- media-storage:/data
# uses the minio CLI to automatically create the required buckets so there is less need to set things up manually
createbuckets:
image: "minio/mc"
depends_on:
- minio
env_file:
- stack.env
entrypoint: >
/bin/sh -c "
sleep 5;
mc alias set minio http://minio:9000 docs_minio_user Fxi3Tf8s45oGt6ofWEzqg0UPbbJpiqRrQBhCv;
mc mb minio/docs-media-storage;
"
volumes:
media-storage:
frontend-nginx:
docs_db:
```
With these env vars (obfuscated for my setup of course):
I spent a lot of time compiling this full list from their docs which assumes multiple files and interpolation of env vars.
```sh
BUCKET_NAME=docs-media-storage
COLLABORATION_WS_URL=wss://example.com/collaboration/ws/
DJANGO_ALLOWED_HOSTS=docs.example.com
DJANGO_SECRET_KEY=<random>
DJANGO_SETTINGS_MODULE=impress.settings
DJANGO_CONFIGURATION=Production
LOGGING_LEVEL_HANDLERS_CONSOLE=ERROR
LOGGING_LEVEL_LOGGERS_ROOT=INFO
LOGGING_LEVEL_LOGGERS_APP=INFO
PYTHONPATH=/app
DJANGO_EMAIL_HOST=mail.example.com
DJANGO_EMAIL_HOST_USER=email-user
DJANGO_EMAIL_HOST_PASSWORD=email-password
DJANGO_EMAIL_PORT=587
[email protected]
DJANGO_EMAIL_USE_SSL=false # A flag to enable or disable SSL for email sending.
DJANGO_EMAIL_BRAND_NAME="Your Brand here"
DJANGO_EMAIL_LOGO_IMG="URL to logo here"
AWS_S3_ENDPOINT_URL=http://minio:9000
AWS_S3_ACCESS_KEY_ID=docs_minio_user
AWS_S3_SECRET_ACCESS_KEY=<random>
AWS_STORAGE_BUCKET_NAME=docs-media-storage
MEDIA_BASE_URL=https://docs.example.com
OIDC_OP_JWKS_ENDPOINT=https://auth.example.com/.well-known/jwks.json
OIDC_OP_AUTHORIZATION_ENDPOINT=https://auth.example.com/authorize
OIDC_OP_TOKEN_ENDPOINT=https://auth.example.com/api/oidc/token
OIDC_OP_USER_ENDPOINT=https://auth.example.com/api/oidc/userinfo
OIDC_OP_LOGOUT_ENDPOINT=https://auth.example.com/api/oidc/end-session
OIDC_RP_CLIENT_ID=<client-id>
OIDC_RP_CLIENT_SECRET=<client-secret>
OIDC_RP_SIGN_ALGO=RS256
OIDC_RP_SCOPES="openid email"
LOGIN_REDIRECT_URL=https://docs.example.com
LOGIN_REDIRECT_URL_FAILURE=https://docs.example.com
LOGOUT_REDIRECT_URL=https://docs.example.com
OIDC_REDIRECT_ALLOWED_HOSTS=["https://docs.example.com"]
DB_HOST=docs_postgres
DB_NAME=docs
DB_USER=docs
DB_PASSWORD=<random>
DB_PORT=5432
POSTGRES_DB=docs
POSTGRES_USER=docs
POSTGRES_PASSWORD=<random>
Y_PROVIDER_API_BASE_URL=http://docs_collab:4444/api/
Y_PROVIDER_API_KEY=<random>
COLLABORATION_SERVER_SECRET=<random>
COLLABORATION_SERVER_ORIGIN=https://docs.example.com
COLLABORATION_API_URL=https://docs.example.com/collaboration/api/
COLLABORATION_BACKEND_BASE_URL=https://docs.example.com
COLLABORATION_LOGGING=true
MINIO_ROOT_USER=docs_minio_user
MINIO_ROOT_PASSWORD=<random>
REDIS_URL=redis://docs_redis:6379/1
DJANGO_CELERY_BROKER_URL=redis://docs_redis:6379/0
```
And, here is the Caddyfile (for just this service):
```ini
docs.example.com {
# Handle WebSocket connections for collaboration
@websocket {
header Connection *Upgrade*
header Upgrade websocket
}
# Proxy to backend for API endpoints
reverse_proxy /api/* docs_backend:8000
# Proxy to backend for admin endpoints
reverse_proxy /admin/* docs_backend:8000
# Proxy to backend for static files
reverse_proxy /static/* docs_backend:8000
# Proxy to backend for media files
reverse_proxy /media/* docs_backend:8000
# WebSocket collaboration server
reverse_proxy /collaboration/ws/* docs_collab:4444
# WebSocket collaboration server
reverse_proxy @websocket docs_collab:4444 {
header_up Host {host}
header_up X-Forwarded-Proto https
header_up Origin {http.request.header.Origin}
}
# Collaboration API endpoints
reverse_proxy /collaboration/api/* docs_collab:4444
# Proxy to frontend for most requests
reverse_proxy docs_frontend:3000
}
```
Once we have the application running in docker, we need to bootstrap the DB (not sure why this isn't done automatically). So, exec into the container and run
```sh
python manage.py migrate
```
After that, we need to create a super user (again, not sure why this isn't done automatically):
```sh
python manage.py createsuperuser --email <admin email> --password <admin password>
```
## References
- [Their docs for setting up with docker-compose](https://github.com/suitenumerique/docs/blob/main/docs/installation/compose.md)
- [Their env var doc](https://github.com/suitenumerique/docs/blob/main/docs/env.md)