Complete Guide: Install MISP on Docker in Linux (Production-Ready)

·

Complete Guide: Install MISP on Docker in Linux (Production-Ready)

MISP is the open-source threat sharing platform powering hundreds of SOCs, CERTs, and threat intel teams worldwide. Running it via Docker eliminates dependency hell, ensures reproducibility, and scales effortlessly.

This guide uses the official MISP/misp-docker repository, which provides production-ready, multi-stage Docker images for misp-core (MISP app + web) and misp-modules (enrichment/expansion modules). Images are automatically built and tagged on GitHub Packages — no manual compilation required.

Tested on: Ubuntu 22.04/24.04 LTS, Debian 12


Prerequisites

Before starting, ensure your Linux system meets the following requirements:

  • Docker 27+ and Docker Compose 2.27+

  • Minimum Resources: 4 vCPU, 8GB RAM, 50GB SSD (16GB+ for production)

  • Ports Open: 80/443 (HTTP/HTTPS), 3306 (MySQL internal), 6379 (Redis internal)

  • Firewall: Allow inbound to your MISP FQDN/IP

  • Domain/SSL: Recommended for production (Let's Encrypt or custom certs)

Install Docker and Docker Compose

Run the following to install Docker on Ubuntu/Debian:

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER

Log out and back in to apply group changes, then install Docker Compose:

sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version

Step 1 — Clone the Repository and Prepare Environment

Create a working directory and clone the official MISP Docker repo:

mkdir ~/misp-docker && cd ~/misp-docker
git clone https://github.com/MISP/misp-docker.git .
cp template.env .env

Open .env in your preferred editor and set the following mandatory variables:

# Database credentials (AlphaNum only — avoid special characters in passwords)
MYSQL_USER=misp
MYSQL_PASSWORD=your_strong_password123
MYSQL_ROOT_PASSWORD=your_root_password123
MYSQL_DATABASE=misp

# Redis
REDIS_PASSWORD=redis_pass123

# MISP Base URL — critical for emails and sync
BASE_URL=https://your-misp.domain.com

# For local testing only
# BASE_URL=http://your-ip:8080

# Worker scaling — adjust based on server load
NUM_WORKERS_DEFAULT=5
NUM_WORKERS_PRIO=5
NUM_WORKERS_EMAIL=2

# Internal Docker network address for MISP Modules
MISP_MODULES_FQDN=http://misp-modules

# Enable DB-based settings storage (recommended for production)
ENABLE_DB_SETTINGS=true

Pro Tip: Use pwgen 32 1 to generate secure random passwords. Avoid special characters like !@#$ in MySQL passwords as they cause configuration parsing errors.


Step 2 — Review and Configure Docker Compose

The repository ships with a default docker-compose.yml. Below is the production-hardened version you should use:

version: '3.8'

services:
  db:
    image: mariadb:11
    restart: always
    volumes:
      - mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    healthcheck:
      test: ["CMD", "mariadb-admin", "--protocol=tcp", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

  redis:
    image: redis:7-alpine
    restart: always
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

  misp-modules:
    image: ghcr.io/misp/misp-docker/misp-modules:latest
    restart: always
    environment:
      - REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379
      - WORKER=off
    depends_on:
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:6666 || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3

  misp-core:
    image: ghcr.io/misp/misp-docker/misp-core:latest
    restart: always
    ports:
      - "8080:80"
      # - "443:443"   # Uncomment when using HTTPS in production
    volumes:
      - configs:/var/www/MISP/app/Config
      - logs:/var/www/MISP/app/tmp/logs
      - files:/var/www/MISP/app/files
      - gnupg:/var/www/MISP/.gnupg
      - ./ssl:/etc/ssl/certs:ro
      - ./ca-certs:/usr/local/share/ca-certificates:ro
    environment:
      - MYSQL_HOST=db
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - REDIS_PASSWORD=${REDIS_PASSWORD}
      - BASE_URL=${BASE_URL}
      - MISP_MODULES_FQDN=${MISP_MODULES_FQDN}
      - NUM_WORKERS_DEFAULT=${NUM_WORKERS_DEFAULT}
      - NUM_WORKERS_PRIO=${NUM_WORKERS_PRIO}
      - NUM_WORKERS_EMAIL=${NUM_WORKERS_EMAIL}
      - ENABLE_DB_SETTINGS=${ENABLE_DB_SETTINGS}
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
      misp-modules:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  mysql_data:
  configs:
  logs:
  files:
  gnupg:

Key Notes on This Configuration

  • Use latest or pin a specific version tag like misp-core:2.4.175-1 from GitHub Packages for reproducibility

  • Use latest-slim tags if you require smaller image footprints for compliance environments

  • All critical data (configs, logs, files, GPG keys) is persisted via named volumes — never lose data on container restart


Step 3 — Pull Images and Initial Launch

Pull all pre-built images from GitHub Container Registry:

docker compose pull

Start all services in detached mode:

docker compose up -d

Watch the bootstrap process (first run takes 5–15 minutes):

docker compose logs -f misp-core

What Happens on First Run

  • Database schema migration runs automatically — fully offline, no manual prompts

  • File permissions are enforced across all volumes

  • Background job workers are initialised (modern replacement for legacy cron)

  • Default admin user is created automatically

Verify the Deployment

curl -k http://localhost:8080
docker compose ps

All services should show status healthy.

Default Login Credentials:

  • URL: http://your-ip:8080 or your configured BASE_URL

  • Email: [email protected]

  • Password: admin

CRITICAL: Change the default admin password immediately after first login.


Step 4 — Production Hardening

SSL Setup

Create the SSL directory:

mkdir ./ssl

For testing, generate a self-signed certificate:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout ssl/key.pem \
  -out ssl/cert.pem \
  -subj "/CN=your-misp.domain.com"

For production, use Let's Encrypt:

sudo apt install certbot
certbot certonly --standalone -d your-misp.domain.com

Then copy the certificates:

cp /etc/letsencrypt/live/your-misp.domain.com/fullchain.pem ./ssl/cert.pem
cp /etc/letsencrypt/live/your-misp.domain.com/privkey.pem ./ssl/key.pem

After setting SSL certs, uncomment port 443:443 in your docker-compose.yml and restart:

docker compose down && docker compose up -d

Custom Root CA Certificates

If your organisation uses internal CAs, mount them in your compose file:

- "./rootca.crt:/usr/local/share/ca-certificates/rootca.crt"

They are automatically installed each time misp-core starts.

LDAP Authentication (Optional)

Add the following to your .env file:

AUTH=LDAP
LDAP_HOST=ldap://your-ldap.server
LDAP_BASE_DN=dc=example,dc=com
LDAP_SEARCH_FILTER=(mail=%s)
LDAP_SEARCH_ATTRIBUTE=mail

Restart the misp-core container to apply:

docker compose restart misp-core

Step 5 — Updates and Maintenance

Updating MISP

Pull the latest images and recreate containers:

cd ~/misp-docker
docker compose pull
docker compose up -d
docker image prune -f

Schema migrations and permission fixes run automatically on each restart — no manual intervention needed.

Backing Up the Database

Stop the stack before backup to ensure consistency:

docker compose down
tar czf misp-mysql-backup-$(date +%Y%m%d).tar.gz \
  /var/lib/docker/volumes/misp-docker_mysql_data/
docker compose up -d

Restoring a Backup

docker compose down
tar xzf misp-mysql-backup-20260225.tar.gz -C /var/lib/docker/volumes/
docker compose up -d

Exporting All Named Volumes

docker compose down
tar czf misp-full-backup-$(date +%Y%m%d).tar.gz \
  /var/lib/docker/volumes/misp-docker_mysql_data/ \
  /var/lib/docker/volumes/misp-docker_configs/ \
  /var/lib/docker/volumes/misp-docker_files/ \
  /var/lib/docker/volumes/misp-docker_gnupg/
docker compose up -d

Step 6 — Advanced Configuration

Custom MISP Settings via Script

Create a customisation script at ./files/customize_misp.sh:

#!/bin/bash
/var/www/MISP/app/cake Admin setSetting "Plugin.ZeroMQ_enable" true
/var/www/MISP/app/cake Admin setSetting "MISP.default_event_distribution" 1
/var/www/MISP/app/cake Admin setSetting "MISP.tagging" true

Make it executable:

chmod +x ./files/customize_misp.sh

Set the path in .env:

CUSTOM_PATH=/custom/customize_misp.sh

This script runs on every container start, making it ideal for enforcing baseline configs.

Checking Worker Status

docker compose exec misp-core /var/www/MISP/app/cake worker status

Running MISP Cake Commands

docker compose exec misp-core /var/www/MISP/app/cake Admin runUpdates
docker compose exec misp-core /var/www/MISP/app/cake Admin updateGalaxies
docker compose exec misp-core /var/www/MISP/app/cake Admin updateTaxonomies
docker compose exec misp-core /var/www/MISP/app/cake Admin updateObjectTemplates 0

Accessing the MISP MySQL Shell Directly

docker compose exec db mariadb -u misp -p misp

Step 7 — Troubleshooting Common Issues

IssueCauseFixContainers not startingMissing .env valuesRe-check mandatory .env varsMySQL password errorsSpecial characters in passwordUse AlphaNum-only passwordsSlow startup on cloudNFS/EFS volume latencyReduce volume mounts to essentials onlyWorkers not runningRedis unhealthyCheck Redis password and health statusModules returning errorsMISP_MODULES_FQDN wrongVerify http://misp-modules is correctSSL errorsCerts not foundConfirm paths in volume mountsGPG errorsgnupg volume emptyVolume auto-initialises on first run

Viewing Service Logs

docker compose logs db
docker compose logs redis
docker compose logs misp-modules
docker compose logs misp-core

Restarting a Single Service

docker compose restart misp-core
docker compose restart misp-modules

Key Takeaways

  • The official misp-docker repo provides pre-built images — you should never need to manually compile MISP from source

  • Always set BASE_URL correctly — it controls email links, sync, and API behaviour

  • An IP or container not reaching misp-modules will silently break enrichment — confirm MISP_MODULES_FQDN=http://misp-modules is set

  • Schema migrations and permission enforcement are fully automated on every container restart

  • Persist your volumes — configs, files, GPG keys, and database data must survive container rebuilds

  • Use specific image tags in production rather than latest to prevent unexpected breaking changes during updates

  • The first run takes 5–15 minutes — do not kill the container during bootstrap


References

  • Official MISP Docker Repository: https://github.com/MISP/misp-docker

  • MISP Project Documentation: https://misp.github.io/MISP/

  • MISP GitHub Packages (Image Registry): https://github.com/orgs/MISP/packages

  • MISP Community Forum: https://misp-community.org


🔗 Stay Connected

Sign up FREE for my web app → https://app.cyberhawkthreatintel.com

  • YouTube: @cyberhawkconsultancy | https://youtube.com/@cyberhawkconsultancy

  • YouTube: @cyberhawkk | https://youtube.com/@cyberhawkk

  • TikTok: @cyberhawkthreatintel | https://tiktok.com/@cyberhawkthreatintel

  • X (Twitter): @cyberhawkintel | https://x.com/cyberhawkintel

  • Telegram: @cyberhawkthreatintel | https://t.me/cyberhawkthreatintel


Hashtags: #MISP #MISPDocker #ThreatIntelligence #CyberSecurity #SOCTools #DockerCompose #LinuxTutorial #IOCSharing #CTI #InfoSec #OpenSource #MISPInstall #ThreatHunting #SecurityOperations #CyberhawkThreatIntel #CyberhawkConsultancy #CyberhawkK