Documentation

Docker Compose

Run AppEngine, MongoDB, and Redis with one compose file — the fastest path to a working self-hosted instance.

The repo ships a docker-compose.yml that brings up AppEngine plus its two dependencies. Five minutes from a clean checkout to a running instance.

What you get

Three services on a private Docker network:

  • appengine_<NODE_ENV> — the NestJS server
  • redis — cache, queues
  • mongodb — primary data store

Plus two named volumes (redis_data, mongodb_data) so data survives container restarts.

This setup is good for development, demos, and single-box production where the box is reliable. For real production with HA, see Kubernetes.

Prerequisites

  • Docker Engine 24+ and Docker Compose v2
  • 8 GB RAM minimum (4 for the AppEngine container, 2 for Mongo, 1 for Redis, 1 headroom)
  • Repository access to ghcr.io/appmint/appengine (or your custom image)
  • An NPM token if you're building from source against private packages

Step-by-step

  1. 1

    Clone the repo (or pull the image)

    If you have repo access:

    git clone https://github.com/appmint/appengine.git
    cd appengine
    

    If you only have the image, create an empty directory and skip ahead — you'll need only the compose file and an env file.

  2. 2

    Build (or skip)

    If you cloned the source, build the image:

    docker compose build --build-arg NPM_TOKEN=$NPM_TOKEN
    

    If you're using a pre-built image from a registry, edit docker-compose.yml and replace the build: block with:

    image: ghcr.io/appmint/appengine:latest
    

    Then docker compose pull.

  3. 3

    Create the env file

    Copy src/envs/development.env to .env at the project root. The compose file reads ${VAR} from there. The minimum vars Compose needs:

    # .env
    NODE_ENV=development
    SERVER_PORT=3300
    
    REDIS_PORT=6379
    REDIS_HOST=redis
    
    MONGODB_PORT=27017
    MONGODB_HOST=mongodb
    MONGODB_USERNAME=admin
    MONGODB_PASSWORD=admin
    MONGODB_CONN=mongodb://admin:admin@mongodb:27017
    REDIS_CONN=redis://redis:6379
    
    ROOT_ORG=fundu
    ROOT_USER=admin
    ROOT_PASSWORD=changemepleaseimnotjokesayinnow
    [email protected]
    

    Plus your secrets — JWT, vendor APIs, OAuth — see Environment variables for the full list.

    Don't ship development.env to production

    The shipped development.env has weak default secrets and live test API keys. Treat it as a starting structure, not a production template. Generate fresh JWT secrets with openssl rand -base64 64.

  4. 4

    Start the stack

    docker compose up -d
    

    First boot pulls or builds, runs npm install (in dev mode), and starts. AppEngine waits for Mongo + Redis health, then runs schema migrations, then opens the port.

    Tail the logs:

    docker compose logs -f appengine
    

    You're ready when you see Nest application successfully started.

    Hit the health endpoint:

    curl http://localhost:3300/monitoring/health
    # {"status":"ok","mongodb":"up","redis":"up"}
    
  5. 5

    Sign in as the root user

    The first boot creates the root org and user from the ROOT_* env vars. Open a browser to http://localhost:3300 (or front it with the websitemint admin).

    Use the credentials you set:

    Org: fundu
    Email: [email protected]
    Password: changemepleaseimnotjokesayinnow
    

    Change the password in Settings → Profile after first login.

  6. 6

    Persist data across rebuilds

    The compose file already declares mongodb_data and redis_data volumes — your data survives docker compose down. To wipe and start over:

    docker compose down -v   # the -v deletes volumes, careful
    

    For backups, mongodump from a sidecar (or docker exec mongodb mongodump):

    docker exec mongodb mongodump \
      --uri "mongodb://admin:admin@localhost:27017" \
      --out /backup/$(date +%Y%m%d)
    

    Mount a host directory into the mongo service's volumes so the dump lands somewhere you can rsync off the box.

Production-ish mode

The shipped compose runs npm run start:debug and mounts the source — that's a dev configuration. For a production-on-one-box setup:

# docker-compose.prod.yml
services:
  appengine:
    image: ghcr.io/appmint/appengine:1.x.x   # pin a version
    container_name: appengine_production
    command: node dist/main.js
    restart: unless-stopped
    networks: [appmint-network]
    environment:
      NODE_ENV: production
      SERVER_PORT: 3300
      MEMORY_LIMIT: '4096'
    env_file:
      - .env.production
    ports:
      - '127.0.0.1:3300:3300'   # bind to localhost; reverse-proxy in front
    depends_on:
      mongodb: { condition: service_healthy }
      redis:   { condition: service_healthy }
    healthcheck:
      test: ['CMD', 'curl', '-f', 'http://localhost:3300/monitoring/health']
      interval: 30s
      timeout: 5s
      retries: 3

  mongodb:
    image: mongo:7
    restart: unless-stopped
    networks: [appmint-network]
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGODB_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_PASSWORD}
    volumes:
      - mongodb_data:/data/db
    healthcheck:
      test: ['CMD', 'mongosh', '--eval', 'db.adminCommand(\"ping\")']
      interval: 30s
      timeout: 5s
      retries: 3

  redis:
    image: redis:7
    restart: unless-stopped
    networks: [appmint-network]
    command: ['redis-server', '--appendonly', 'yes']
    volumes:
      - redis_data:/data
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 30s
      timeout: 5s
      retries: 3

networks:
  appmint-network: { driver: bridge }

volumes:
  mongodb_data:
  redis_data:

Run with:

docker compose -f docker-compose.prod.yml up -d

The differences from dev:

  • Pinned image, no source mount, no debug command
  • Health checks on every service, depends-on with health condition
  • Restart on failure
  • Mongo/Redis bound to internal network only — only AppEngine is exposed
  • AppEngine bound to localhost — fronted by host nginx/Traefik/Caddy for TLS