Save products you love by clicking the heart icon.
Download the free DevOps Career Roadmap PDF - A complete 8-month learning path from Linux fundamentals to AI infrastructure, with certification checklists and production stack recommendations.
A practical guide to DevOps certifications in 2026. Compare LPI DevOps Tools Engineer, CKAD, CKA, Terraform Associate, and AWS DevOps Engineer — which to choose, what they cover, how they map to real-world skills, and how they fit together in a career path from beginner to expert.
SOGo 6 is a ground-up rewrite of the SOGo groupware suite. After 20 years of AngularJS and Objective-C in SOGo 5, the new version ships a Python/Flask REST API backend, a Next.js 16 frontend, PostgreSQL for persistence, Redis for caching and task queues, and a proper admin API replacing the old sogo-tool CLI. The first public beta is planned for summer 2026.
This article walks through a production-representative Docker Compose stack that wires SOGo 6 together with Stalwart (an all-in-one IMAP/SMTP/Sieve mail server) and OpenLDAP (for user authentication). The full source is at github.com/tobias-weiss-ai-xr/sogo6-stalwart-openldap-dockerized.
The stack runs eight services on a single Docker network:
┌──────────────────┐ ┌──────────────────┐
│ SOGo 6 UI │ │ SOGo 6 Server │
│ Next.js :3000 │◄───►│ Flask :5000 │
└──────────────────┘ └──────┬───────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PostgreSQL │ │ Redis │ │ OpenLDAP │
│ :5432 │ │ :6379 │ │ :389 │
└──────────────┘ └──────────────┘ └──────┬───────┘
│
▼
┌──────────────┐
│ Stalwart │
│ IMAP/SMTP │
│ /Sieve │
└──────────────┘
An Nginx reverse proxy terminates TLS on ports 80/443, routing /api/ to the Flask backend and everything else to the Next.js frontend. A MailDev container catches outgoing mail for inspection during testing.
Each service has resource limits, health checks, and dependency ordering defined in docker-compose.yaml. The total memory footprint sits around 2 GB.
The backend is a Gunicorn/Flask application serving a RESTful API. It connects to PostgreSQL for data persistence (calendars, address books, user preferences), Redis for session caching and the Celery task queue, and OpenLDAP for authentication.
Core environment variables:
SOGO_DB_URI: postgresql://sogo:sogo@sogo6-postgres:5432/sogo
SOGO_REDIS_URI: redis://sogo6-redis:6379/0
SOGO_LDAP_URI: ldap://sogo6-ldap:389
SOGO_LDAP_BASE_DN: dc=example,dc=org
SOGO_LDAP_BIND_DN: cn=admin,dc=example,dc=org
SOGO_SMTP_SERVER: sogo6-stalwart
SOGO_SMTP_PORT: 20025
SOGo 6 splits its configuration into two layers:
process.conf file. The container mounts this read-only at /etc/sogo/process.conf.The server exposes two API surface areas:
| API | Base Path | Purpose |
|---|---|---|
| User API | /api/user/v1/ | Mail, calendar, contacts for authenticated users |
| Admin API | /api/admin/v1/ | System configuration, user source management, domain settings |
The frontend is a Next.js 16 application (React 19, TypeScript, Tailwind CSS). Its only configuration is the API base URL — it proxies all data requests to the Flask backend. The UI container is stateless and can be scaled independently.
Stalwart is an all-in-one mail server written in Rust. It replaces the traditional stack of Postfix + Dovecot + Rspamd with a single binary supporting every standard mail protocol:
In this stack, Stalwart uses PostgreSQL as its storage backend (separate database from SOGo). The configuration is a single JSON file:
{
"@type": "PostgreSql",
"host": "sogo6-postgres",
"port": 5432,
"database": "stalwart",
"authUsername": "stalwart",
"authSecret": {
"@type": "Value",
"secret": "stalwart_sogo6_2026"
}
}
Stalwart v0.16 (used here) introduced a unified JMAP-based management API and a stalwart-cli apply command for declarative configuration — fitting naturally into infrastructure-as-code workflows.
Port mappings expose all mail protocols to the host for client testing:
| Internal Port | Host Port | Protocol |
|---|---|---|
| 25 | 20025 | SMTP |
| 465 | 20465 | SMTPS |
| 587 | 20587 | Submission |
| 143 | 20143 | IMAP |
| 993 | 20993 | IMAPS |
| 4190 | 24190 | ManageSieve |
OpenLDAP provides the user directory. The Docker image is custom-built with seed data baked in — three test users (two standard, one admin) loaded from an LDIF file on first start.
dn: uid=testuser@example.org,ou=users,dc=example,dc=org
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: testuser@example.org
cn: Test User
sn: User
mail: testuser@example.org
userPassword: password123
The health check runs an LDAP search against the base DN to verify slapd is responsive.
One caveat: OpenLDAP's ch_calloc assertion fails when Docker sets a high ulimit -n. The custom entrypoint script works around this by clamping to ulimit -n 1024 before starting slapd.
Nginx terminates TLS (self-signed certificates generated by the included gen-certs.sh script) and routes traffic:
/api/* → sogo6-server:5000 (Flask backend)/* → sogo6-ui:3000 (Next.js frontend)The configuration supports WebSocket upgrade headers for Server-Sent Events and future real-time features.
/api/user/v1/auth/loginSOGO_LDAP_BIND_DN and verifies the user's password against the inetOrgPerson entryAuthorization: Bearer headersogo6-stalwart:25)http://localhost:1080 for inspectionSOGo 6 implements CalDAV and CardDAV servers. Calendar events and address book entries are stored in PostgreSQL. LDAP contacts are read from OpenLDAP in real-time.
git clone https://github.com/tobias-weiss-ai-xr/sogo6-stalwart-openldap-dockerized.git
cd sogo6-stalwart-openldap-dockerized
bash sogo6/scripts/setup.sh
The setup script:
secrets/sogo6.vault.env, gitignored)sogo6/nginx/certs/)docker compose up -d
Docker Compose starts services in dependency order — PostgreSQL, Redis, LDAP, and Stalwart become healthy before the Flask backend starts, and the backend must be healthy before the Next.js frontend launches.
bash sogo6/scripts/init-sogo6.sh
This script calls the Admin API to configure:
example.orgdocker compose ps # All 8 services healthy
curl http://localhost:3000/env # Next.js frontend responds
curl http://localhost:5001/api/user/v1/system # Flask API responds
curl http://localhost:1080 # MailDev web UI
| Service | URL |
|---|---|
| SOGo 6 UI | http://localhost:3000 |
| User API | http://localhost:5001/api/user/v1/system |
| Admin API | http://localhost:5001/api/admin/v1/auth/login |
| Swagger (User) | http://localhost:5001/swagger-basic |
| Swagger (Admin) | http://localhost:5001/swagger-admin |
| MailDev | http://localhost:1080 |
| Nginx (TLS) | https://localhost |
| Username | Password | Role |
|---|---|---|
testuser@example.org | password123 | Standard user |
testadmin@example.org | password123 | Admin (sogoAdminRole) |
testuser2@example.org | password123 | Standard user |
The repository ships 129 shell-based integration tests that require nothing beyond bash, curl, and openssl. They cover:
| Suite | Checks | What It Validates |
|---|---|---|
docker-test.sh | 6 | Container health, restarts, uptime, network connectivity |
api-test.sh | 15 | API health, Swagger, auth (all 3 users), negative tests |
smtp-test.sh | 14 | SMTP EHLO, submission, IMAP login, Sieve, TLS protocol |
ldap-test.sh | 6 | LDAP reachability, users present, mail attributes |
postgres-test.sh | 6 | DB connectivity, schema, server version |
nginx-test.sh | 8 | HTTP/HTTPS proxy, TLS cert, security headers |
redis-test.sh | 7 | PING, SET/GET, memory, connected clients |
integration-test.sh | 9 | Cross-service token flow, CORS, DNS resolution |
security-test.sh | 12 | Port exposure, container user, TLS validity, secrets leakage |
script-test.sh | 15 | Config integrity, shebangs, JSON validity, cert-key match |
Run everything with bash tests/run-all-tests.sh. Python integration tests (pytest + psycopg2) and Playwright E2E tests are available as optional extras.
bash sogo6/scripts/manage-secrets.sh # Generate vault
bash sogo6/scripts/manage-secrets.sh --list # List masked secrets
bash sogo6/scripts/manage-secrets.sh --env-file # Export for docker-compose
bash sogo6/scripts/backup.sh
Creates a timestamped archive of all Docker volumes (PostgreSQL, Redis, Stalwart, LDAP data and config) to ./backups/.
docker compose down -v
docker compose up -d
bash sogo6/scripts/init-sogo6.sh
For production testing, replace the self-signed certs in sogo6/nginx/certs/ with real certificates and regenerate:
bash sogo6/scripts/gen-certs.sh
| Aspect | SOGo 5 | SOGo 6 |
|---|---|---|
| Frontend | AngularJS | Next.js 16, React 19, TypeScript |
| Backend | Objective-C / GNUstep | Python 3.10+, Flask, Gunicorn |
| Configuration | sogo.conf file | Process settings (env/file) + dynamic settings (DB + Admin API) |
| Cache | Memcached | Redis (session cache + Celery task queue) |
| Database | MySQL/MariaDB/PostgreSQL | PostgreSQL required |
| Admin tool | sogo-tool CLI | RESTful Admin API + web UI |
| Mobile sync | ActiveSync | MAPI over HTTP (ActiveSync planned post-release) |
| Architecture | Monolithic | Modular (API → Interface → Module → Manager) |
| Deployment | Single container | Minimum three containers (UI, server, agent) |
| Migration | — | Scripts provided; old sogo.conf importable via Admin API |
The SOGo team announced at FOSDEM 2026 that the public beta is planned for summer 2026. The initial release covers the complete web application (mail, calendar, contacts) but will not include the desktop sync server initially.
All code is open source under the MIT licence: