@mattermost/testcontainers
v0.2.1
Published
CLI and library for managing Mattermost test environments using Docker containers with automatic port allocation, subpath routing, HA clustering, and isolated dependencies
Downloads
592
Readme
@mattermost/testcontainers
CLI and library for spinning up Mattermost test environments using Docker containers. Handles automatic port allocation, networking, health checks, and dependency orchestration so you can focus on testing.
Requirements
- Docker running locally
- Node.js 24+
Install
npm install @mattermost/testcontainersOr run directly with npx:
npx @mattermost/testcontainers startQuick Start
Start a Mattermost server with PostgreSQL and Inbucket (email):
npx @mattermost/testcontainers startThis starts three containers on a shared Docker network, waits for health checks, configures default test settings via mmctl, and prints connection info:
Connection Information:
=======================
Mattermost: http://localhost:34025
PostgreSQL: postgres://mmuser:mostest@localhost:34015/mattermost_test?sslmode=disable
Inbucket: http://localhost:34016Stop everything:
npx @mattermost/testcontainers stopCLI Commands
start
Start the test environment.
npx @mattermost/testcontainers start [options]| Option | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| -e, --edition <edition> | Server edition: enterprise (default), fips, team |
| -t, --tag <tag> | Image tag (e.g., master, release-11.4) |
| -D, --deps <deps> | Additional dependencies, comma-separated: openldap, keycloak, minio, elasticsearch, opensearch, redis, dejavu, prometheus, grafana, loki, promtail, libretranslate |
| --deps-only | Start dependencies only (no Mattermost server) |
| --ha | High-availability mode (3-node cluster with nginx) |
| --subpath | Subpath mode (two servers at /mattermost1 and /mattermost2) |
| --entry | Mattermost Entry tier |
| --esr | Use the current 10.11 ESR (Extended Support Release) version |
| --admin [username] | Create admin user (default: sysadmin) |
| --admin-password <pw> | Admin password (default: Sys@dmin-sample1) |
| -E, --env <KEY=value> | Environment variable for Mattermost (repeatable) |
| --env-file <path> | Env file for Mattermost server variables |
| -S, --service-env <env> | Service environment: test (default), production, dev |
| -c, --config <path> | Path to config file |
| -o, --output-dir <dir> | Output directory (default: .tc.out) |
stop
Stop all containers from the current session.
npx @mattermost/testcontainers stoprestart
Restart all containers. Stops Mattermost first, restarts dependencies, waits for readiness, then starts Mattermost.
npx @mattermost/testcontainers restartrm
Stop and remove containers from the current session.
npx @mattermost/testcontainers rm [-y]rm-all
Remove all testcontainers resources (finds containers and networks with the org.testcontainers=true Docker label).
npx @mattermost/testcontainers rm-all [-y]upgrade
Upgrade Mattermost to a new image tag in-place (preserves network, ports, environment).
npx @mattermost/testcontainers upgrade -t release-11.5info
Display available dependencies, configuration options, and examples.
npx @mattermost/testcontainers infoCLI Examples
Basic Server
# Default enterprise edition, master tag
npx @mattermost/testcontainers start
# Specific version
npx @mattermost/testcontainers start -t release-11.4
# Team edition
npx @mattermost/testcontainers start -e team -t release-11.4
# ESR version (currently 10.11)
npx @mattermost/testcontainers start --esr
# With admin user
npx @mattermost/testcontainers start --admin sysadmin --admin-password 'Sys@dmin-sample1'With Additional Dependencies
# Add LDAP
npx @mattermost/testcontainers start -D openldap
# Add S3 storage and search
npx @mattermost/testcontainers start -D minio,elasticsearch
# Full stack (requires MM_LICENSE)
npx @mattermost/testcontainers start -D openldap,minio,elasticsearch,redis,keycloak
# Monitoring stack
npx @mattermost/testcontainers start -D prometheus,grafana,loki,promtailHA Mode (3-Node Cluster)
Requires MM_LICENSE environment variable.
export MM_LICENSE="your-license-string"
# HA cluster with nginx load balancer
npx @mattermost/testcontainers start --ha
# HA with dependencies
npx @mattermost/testcontainers start --ha -D minio,elasticsearch,redisOutput:
Mattermost HA: http://localhost:34025 (nginx load balancer)
Cluster: mm_test_cluster
leader: http://localhost:34026
follower: http://localhost:34027
follower2: http://localhost:34028Subpath Mode (Two Servers Behind Nginx)
Two independent servers behind a single nginx reverse proxy. Server 1 (/mattermost1) is fully configurable. Server 2 (/mattermost2) is a bare, unconfigured instance with its own database.
# Basic subpath
npx @mattermost/testcontainers start --subpath
# Subpath with admin on server1 only
npx @mattermost/testcontainers start --subpath --admin
# Subpath with HA server1 (requires MM_LICENSE)
npx @mattermost/testcontainers start --subpath --ha
# Subpath with dependencies on server1
npx @mattermost/testcontainers start --subpath -D minio,openldapOutput:
Mattermost: http://localhost:34063 (nginx with subpaths)
Server 1: http://localhost:34063/mattermost1
Direct: http://localhost:34064
Server 2: http://localhost:34063/mattermost2 (bare)
Direct: http://localhost:34067Dependencies Only (Local Development)
Start only the dependencies and run Mattermost locally:
npx @mattermost/testcontainers start --deps-only
# Source the generated env file, then run your local server
source .tc.out/.env.tc
make run-serverEnvironment Variables and Feature Flags
# Set feature flags
npx @mattermost/testcontainers start -E MM_FEATUREFLAGS_MOVETHREADSENABLED=true
# Multiple env vars
npx @mattermost/testcontainers start \
-E MM_SERVICESETTINGS_ENABLEOPENSERVER=false \
-E MM_TEAMSETTINGS_MAXUSERSPERTEAM=50
# From env file
npx @mattermost/testcontainers start --env-file ./test.envConfiguration File
Create mattermost-testcontainers.config.mjs in your project root:
import {defineConfig} from '@mattermost/testcontainers';
export default defineConfig({
server: {
edition: 'enterprise',
tag: 'release-11.4',
ha: false,
subpath: false,
env: {
MM_FEATUREFLAGS_MOVETHREADSENABLED: 'true',
},
config: {
ServiceSettings: {
EnableOpenServer: false,
},
TeamSettings: {
MaxUsersPerTeam: 50,
},
},
},
dependencies: ['postgres', 'inbucket', 'openldap', 'minio'],
admin: {
username: 'sysadmin',
password: 'Sys@dmin-sample1',
},
});JSONC format is also supported as mattermost-testcontainers.config.jsonc.
Configuration Priority
Settings are resolved from multiple sources. When the same setting is defined in more than one source, the highest-priority source wins.
| Priority | Source | Example |
| ----------- | --------------------- | -------------------------------------------------------- |
| 1 (highest) | CLI flags | --edition team, -t release-11.4, -D openldap,minio |
| 2 | Environment variables | TC_EDITION=team, TC_SERVER_TAG=release-11.4 |
| 3 | Config file | mattermost-testcontainers.config.mjs or .jsonc |
| 4 (lowest) | Built-in defaults | enterprise edition, master tag, postgres + inbucket |
For example, if your config file sets server.tag: 'release-11.4' but you run with -t master, the CLI flag wins and the master tag is used.
Special cases:
MM_LICENSEmust come from an environment variable — it cannot be set in the config file (rejected to prevent accidental commits).TC_SERVER_IMAGEoverrides the computed edition + tag image entirely (e.g.,TC_SERVER_IMAGE=myregistry/custom:latest).-D/TC_DEPENDENCIESadds to dependencies from the config file rather than replacing them.
Configuration Reference
| Key | Type | Default | Description |
| --------------------------- | ---------- | -------------------------- | ----------------------------------------- |
| server.edition | string | 'enterprise' | enterprise, fips, or team |
| server.tag | string | 'master' | Docker image tag |
| server.ha | boolean | false | Enable HA mode |
| server.subpath | boolean | false | Enable subpath mode |
| server.entry | boolean | false | Enable Entry tier |
| server.serviceEnvironment | string | 'test' | test, production, or dev |
| server.imageMaxAgeHours | number | 24 | Max hours before re-pulling :master tag |
| server.env | object | {} | Mattermost MM_* environment variables |
| server.config | object | {} | Server config patches (applied via mmctl) |
| dependencies | string[] | ['postgres', 'inbucket'] | Enabled dependencies |
| images | object | {} | Override default container images |
| outputDir | string | '.tc.out' | Output directory |
| admin.username | string | — | Admin username to create |
| admin.password | string | 'Sys@dmin-sample1' | Admin password |
Image Overrides
Override default images in the config file:
export default defineConfig({
images: {
postgres: 'postgres:16',
mattermost: 'mattermostdevelopment/mattermost-enterprise-edition:release-11.5',
},
});Or via environment variables: TC_POSTGRES_IMAGE, TC_MINIO_IMAGE, TC_SERVER_IMAGE, etc.
Using in Tests
Approach 1: Embedded (Playwright)
Import MattermostTestEnvironment in your global setup. The environment starts before tests and stops after. Gated by an environment variable so it's opt-in.
global-setup.ts (actual pattern used by e2e-tests/playwright):
import {discoverAndLoadConfig, MattermostTestEnvironment} from '@mattermost/testcontainers';
let environment: MattermostTestEnvironment | null = null;
export default async function globalSetup() {
// Only start containers when PW_TC=true
if (process.env.PW_TC === 'true') {
const config = await discoverAndLoadConfig();
environment = new MattermostTestEnvironment(config);
await environment.start();
// Set base URL for Playwright
process.env.PW_BASE_URL = environment.getServerUrl();
}
// ... other setup (admin user creation, etc.)
// Return teardown function — Playwright calls this after all tests
return async function globalTeardown() {
if (environment) {
await environment.stop();
environment = null;
}
};
}playwright.config.ts:
import {defineConfig} from '@playwright/test';
export default defineConfig({
globalSetup: './global-setup.ts',
use: {
baseURL: process.env.PW_BASE_URL || 'http://localhost:8065',
},
});Run with testcontainers:
PW_TC=true npx playwright testApproach 2: Separate Script (Cypress, etc.)
Start the environment as a separate process before running tests. The CLI writes connection info to .tc.out/ which your tests can read.
package.json:
{
"scripts": {
"test:setup": "npx @mattermost/testcontainers start --admin",
"test:teardown": "npx @mattermost/testcontainers stop",
"test:run": "cypress run",
"test": "npm run test:setup && npm run test:run; npm run test:teardown"
}
}Read connection info in Cypress (cypress.config.ts):
import {defineConfig} from 'cypress';
import * as fs from 'fs';
// Read container info written by the CLI
const dockerInfo = JSON.parse(fs.readFileSync('.tc.out/.tc.docker.json', 'utf-8'));
const mmContainer = dockerInfo.containers.mattermost || dockerInfo.containers['mattermost-server1'];
const serverUrl = mmContainer?.url || 'http://localhost:8065';
export default defineConfig({
e2e: {
baseUrl: serverUrl,
},
env: {
// Pass connection info to tests
INBUCKET_URL: dockerInfo.containers.inbucket
? `http://${dockerInfo.containers.inbucket.host}:${dockerInfo.containers.inbucket.webPort}`
: undefined,
},
});Or source the env file:
# Start environment
npx @mattermost/testcontainers start --admin
# Source environment variables
source .tc.out/.env.tc
# Run tests with env vars available
cypress runApproach 3: Programmatic (Node.js Test Scripts)
Use the library API for full control in custom test scripts:
import {MattermostTestEnvironment, discoverAndLoadConfig} from '@mattermost/testcontainers';
async function main() {
const config = await discoverAndLoadConfig();
const env = new MattermostTestEnvironment(config);
await env.start();
const serverUrl = env.getServerUrl();
console.log(`Server running at ${serverUrl}`);
// Access specific service info
const info = env.getConnectionInfo();
console.log(`PostgreSQL: ${info.postgres.connectionString}`);
// Run mmctl commands
const mmctl = env.getMmctl();
await mmctl.exec('user create --email [email protected] --username testuser --password Test123! --system-admin');
// ... run your tests ...
await env.stop();
}
main().catch(console.error);Environment Variables
| Variable | Description |
| ------------------------ | -------------------------------------------------------- |
| MM_LICENSE | Enterprise license (required for --ha and redis) |
| TC_EDITION | Server edition |
| TC_SERVER_TAG | Server image tag |
| TC_SERVER_IMAGE | Full server image (overrides edition + tag) |
| TC_DEPENDENCIES | Dependencies (comma-separated) |
| TC_HA | Enable HA mode (true/false) |
| TC_SUBPATH | Enable subpath mode (true/false) |
| TC_ENTRY | Enable Entry tier (true/false) |
| TC_ADMIN_USERNAME | Admin username |
| TC_ADMIN_PASSWORD | Admin password |
| TC_OUTPUT_DIR | Output directory |
| TC_IMAGE_MAX_AGE_HOURS | Max hours before re-pulling :master images |
| TC_<SERVICE>_IMAGE | Override image for a service (e.g., TC_POSTGRES_IMAGE) |
Supported Dependencies
| Dependency | Description | Default Image |
| ---------------- | --------------------------------------------------------------- | ------------------------------------------------------ |
| postgres | PostgreSQL database (always included) | postgres:14 |
| inbucket | Email testing (SMTP + web UI, always included) | inbucket/inbucket:stable |
| openldap | LDAP authentication | osixia/openldap:1.4.0 |
| keycloak | SAML/OIDC identity provider | quay.io/keycloak/keycloak:23.0.7 |
| minio | S3-compatible object storage | minio/minio:RELEASE.2024-06-22T05-26-45Z |
| elasticsearch | Search engine | mattermostdevelopment/mattermost-elasticsearch:8.9.0 |
| opensearch | Search engine (alternative) | mattermostdevelopment/mattermost-opensearch:2.7.0 |
| redis | Cache (requires MM_LICENSE) | redis:7.4.0 |
| dejavu | Elasticsearch web UI (requires elasticsearch or opensearch) | appbaseio/dejavu:3.4.2 |
| prometheus | Metrics collection | prom/prometheus:v2.46.0 |
| grafana | Dashboards (requires prometheus or loki) | grafana/grafana:10.4.2 |
| loki | Log aggregation (requires promtail) | grafana/loki:3.0.0 |
| promtail | Log shipping (requires loki) | grafana/promtail:3.0.0 |
| libretranslate | Translation service | libretranslate/libretranslate:v1.8.4 |
Output Directory
After start, the output directory (default .tc.out/) contains:
.tc.out/
.env.tc # Sourceable environment variables
.tc.docker.json # Container metadata (IDs, ports, images)
.tc.server.config.json # Server configuration dump
logs/
postgres.log
inbucket.log
mattermost.log
...Default Credentials
| Service | Username | Password |
| ---------------------- | ------------------------------- | ------------------ |
| PostgreSQL | mmuser | mostest |
| Admin (when --admin) | sysadmin | Sys@dmin-sample1 |
| Keycloak admin | admin | admin |
| Keycloak test users | user-1, user-2 | Password1! |
| OpenLDAP admin | cn=admin,dc=mm,dc=test,dc=com | mostest |
| OpenLDAP test users | test.one, test.two | Password1 |
| MinIO | minioaccesskey | miniosecretkey |
Security Considerations
This CLI is designed for controlled environments — developer machines and CI pipelines. It is not intended for production use.
Hardcoded Test Credentials
All service credentials listed above are hardcoded defaults intended for local testing only. They match the upstream Mattermost docker-compose test configuration. These credentials are:
- Written to
.tc.out/.env.tcand.tc.out/.tc.docker.jsonin plaintext - Printed to console output (admin credentials when
--adminis used) - Visible in CI logs if captured
Do not use these credentials in any production or staging environment. Add .tc.out/ to your .gitignore to prevent accidental commits of output files.
Enterprise License
MM_LICENSE must be provided via environment variable (not config files) to prevent accidental leakage in committed configuration. The CLI explicitly rejects MM_LICENSE in config file server.env sections.
Docker Commands
All Docker CLI invocations use execFileSync (array-form arguments) to prevent shell injection. Container IDs read from .tc.docker.json are validated against ^[a-f0-9]{12,64}$ before use. Output directory paths are validated to prevent path traversal before any recursive deletion.
Network Exposure
Containers bind to 0.0.0.0 (default testcontainers behavior) with dynamically allocated ports. On shared networks, services may be accessible to other machines. This is standard for local Docker development but should be considered in shared CI environments.
License
See LICENSE.txt in the project root for details.
