Skip to content

Open edX Instructor Dev Environment Installation with Tutor

Not sure where to start? Read getting-started.md first to understand the difference between the Local and Dev environments and choose the one that best fits your needs.

This guide provides step-by-step instructions for setting up an isolated Open edX development environment specifically for testing and developing the Instructor Dashboard MFE (frontend-app-instruct). It combines the isolation pattern of the standard dev environment with the instructor-specific plugin and MFE configuration.

Note for LLM CLI tools (Claude Code, etc.): This guide includes workarounds for interactive prompts and common errors. Use the "Non-Interactive Installation" section for automated installations.

IMPORTANT for LLM assistants: After completing the installation and creating users, you MUST provide the user with a summary including:

  1. All access URLs (LMS, Studio, Django Admin, Instructor MFE)
  2. Admin credentials (username and password)
  3. Student credentials (username and password)
  4. Instructions on how to start/stop the platform and the MFE

See the Post-Installation Summary Template section for the exact format to use.


Table of Contents


When to Use This Guide

Use this guide if you want to:

  • Develop or test the Instructor Dashboard MFE (frontend-app-instruct)
  • Test instructor-specific features (Open Responses, gradebook, student data, etc.)
  • Work on the LMS backend code that powers the Instructor tab
  • Run the MFE locally against a real Open edX backend with hot-reload

Use the standard Dev guide if you want to:

  • Modify Open edX Python/Django code without needing the Instructor MFE
  • Develop general features or fix bugs unrelated to the Instructor Dashboard
  • Contribute to the Open edX project in areas outside the Instructor feature
AspectDev EnvironmentInstructor Dev Environment
PurposeGeneral Open edX developmentInstructor Dashboard MFE development
Backendtutor dev with edx-platform mountedtutor dev with edx-platform mounted
Frontend MFENoYes — frontend-app-instruct on port 2003
Instructor pluginNoYes — CORS, CSRF, and URL config for MFE
devstack.pyUnmodifiedINSTRUCTOR_MICROFRONTEND_URL added
Ports8000 (LMS), 8001 (Studio)8000 (LMS), 8001 (Studio), 2003 (MFE)

System Requirements

Operating System

  • Any 64-bit UNIX-based OS (Linux, macOS)
  • Windows with WSL 2
  • AMD64 and ARM64 architectures supported

Required Software

  • Docker v24.0.5 or higher (with BuildKit 0.11+)
  • Docker Compose v2.0.0 or higher
  • Python 3.9 or higher
  • Node.js 18 or higher (for the Instructor MFE)
  • npm 8 or higher
  • Git

Hardware Requirements

ResourceMinimumRecommended
RAM4 GB8 GB
CPU24
Disk10 GB30 GB
  • Ports 8000, 8001, and 2003 must be available

Docker Desktop RAM Configuration (Important)

Before installing, ensure Docker has enough RAM allocated:

  1. Open Docker Desktop
  2. Go to Settings (gear icon)
  3. Navigate to Resources
  4. Set Memory to at least 4 GB (recommended: 6 GB)
  5. Click Apply & Restart

Note for macOS: Docker Desktop on macOS may show incorrect RAM readings. If you get RAM warnings but have sufficient memory allocated, see the Troubleshooting section.

Configure /etc/hosts (Required)

The local domains need to resolve to localhost. Add these entries to your /etc/hosts file:

bash
sudo sh -c 'echo "127.0.0.1 local.openedx.io studio.local.openedx.io apps.local.openedx.io meilisearch.local.openedx.io" >> /etc/hosts'

Verify the configuration:

bash
grep openedx /etc/hosts

Expected output:

127.0.0.1 local.openedx.io studio.local.openedx.io apps.local.openedx.io meilisearch.local.openedx.io

Note: Without this configuration, the URLs won't resolve and you won't be able to access the platform or the Instructor MFE.


Understanding TUTOR_ROOT

TUTOR_ROOT is the directory where Tutor stores all its configuration and data. By pointing each installation to a different directory, you get fully isolated instances.

TUTOR_ROOT/
├── config.yml      # Main configuration (hostnames, passwords, plugins)
├── data/           # Persistent data (MySQL, MongoDB, uploaded files)
│   ├── mysql/
│   ├── mongodb/
│   ├── redis/
│   └── ...
└── env/            # Generated environment files
  • Different TUTOR_ROOT = Different installation
  • Each TUTOR_ROOT has its own configuration, data, and Docker containers
  • This allows running multiple isolated Open edX instances on the same machine

Installation Steps

Step 1: Create Project Directory

bash
mkdir -p ~/openedx-instructor
cd ~/openedx-instructor

Step 2: Clone Required Repositories

bash
# Clone the Open edX platform
git clone https://github.com/openedx/openedx-platform.git

# Clone Tutor (for editable installation)
git clone https://github.com/overhangio/tutor.git

# Clone the Instructor Dashboard MFE
git clone https://github.com/openedx/frontend-app-instruct.git

# Switch Tutor to the latest stable release
cd tutor
git checkout v21.0.2  # Or the latest stable version
cd ..

Important: Always use a stable Tutor release tag (e.g., v21.0.2) rather than the master branch for stability. Check the latest release at https://github.com/overhangio/tutor/releases

Step 3: Select Branch for edx-platform

bash
cd openedx-platform

# For latest development code (next release)
git switch master

# OR for stable release (Ulmo)
# git switch open-release/ulmo.master

cd ..

Step 4: Create Python Virtual Environment

bash
python3 -m venv .venv
source .venv/bin/activate

Step 5: Install Tutor in Editable Mode

Installing Tutor in editable mode allows you to modify Tutor itself if needed (useful for workarounds like the macOS RAM fix).

bash
pip install -U pip
pip install -e "./tutor[full]"

Step 6: Set TUTOR_ROOT (Important for Isolation)

bash
export TUTOR_ROOT=~/openedx-instructor

Step 7: Mount the edx-platform Repository

bash
tutor mounts add ~/openedx-instructor/openedx-platform

Verify the mount:

bash
tutor mounts list

Step 8: Build the Development Image

bash
tutor images build openedx-dev

Step 9: Create the Instructor Plugin

Get the Tutor plugins directory and create the plugin file there:

bash
tutor plugins printroot

Create a file named instructor.py in that directory with the following content:

python
from tutor import hooks

hooks.Filters.ENV_PATCHES.add_item(
    (
        "openedx-lms-development-settings",
        """
CORS_ORIGIN_WHITELIST.append("http://apps.local.openedx.io:2003")
LOGIN_REDIRECT_WHITELIST.append("apps.local.openedx.io:2003")
CSRF_TRUSTED_ORIGINS.append("http://apps.local.openedx.io:2003")
INSTRUCTOR_MICROFRONTEND_URL = "http://apps.local.openedx.io:2003"
        """
    )
)

hooks.Filters.ENV_PATCHES.add_item(
    (
        "openedx-cms-development-settings",
        """
CORS_ORIGIN_WHITELIST.append("http://apps.local.openedx.io:2003")
LOGIN_REDIRECT_WHITELIST.append("apps.local.openedx.io:2003")
CSRF_TRUSTED_ORIGINS.append("http://apps.local.openedx.io:2003")
INSTRUCTOR_MICROFRONTEND_URL = "http://apps.local.openedx.io:2003"
        """
    )
)

Note: The plugin file lives in the global Tutor plugins directory, not in TUTOR_ROOT. It will only be active for this instance because you enable it here in Step 10.

Step 10: Configure INSTRUCTOR_MICROFRONTEND_URL in devstack.py

Add the following line at the end of openedx-platform/lms/envs/devstack.py (before any warning or comment at the bottom of the file):

python
INSTRUCTOR_MICROFRONTEND_URL = 'http://apps.local.openedx.io:2003'

Step 11: Enable Plugins

bash
tutor plugins enable mfe
tutor plugins enable instructor
tutor config save

Step 12: Launch the Development Environment

bash
tutor dev launch

This command will:

  1. Stop any existing locally-running Tutor containers
  2. Configure the platform for development
  3. Disable HTTPS
  4. Start all services in development mode
  5. Run database migrations

Step 13: Install MFE Dependencies and Start It

In a separate terminal:

bash
cd ~/openedx-instructor/frontend-app-instruct
npm install
npm run dev

The MFE will start on port 2003.

Step 14: Access the Platform

ServiceURL
LMShttp://local.openedx.io:8000
Studiohttp://studio.local.openedx.io:8001
Django Adminhttp://local.openedx.io:8000/admin
Instructor MFEhttp://apps.local.openedx.io:2003

The tutor dev launch command is interactive and may fail when run from CLI tools or scripts. Use these steps instead:

Step 1: Set up environment

bash
# Create project directory
mkdir -p ~/openedx-instructor
cd ~/openedx-instructor

# Clone repositories
git clone https://github.com/openedx/openedx-platform.git
git clone https://github.com/overhangio/tutor.git
git clone https://github.com/openedx/frontend-app-instruct.git

# Switch Tutor to stable release
cd tutor && git checkout v21.0.2 && cd ..

# Switch openedx-platform to desired branch
cd openedx-platform && git switch master && cd ..

# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate

# Install Tutor in editable mode
pip install -U pip
pip install -e "./tutor[full]"

# Set TUTOR_ROOT
export TUTOR_ROOT=~/openedx-instructor

Step 2: Mount repository and configure

bash
# Mount edx-platform
tutor mounts add ~/openedx-instructor/openedx-platform

# Configure Tutor (non-interactive)
tutor config save \
  --set LMS_HOST=local.openedx.io \
  --set CMS_HOST=studio.local.openedx.io \
  --set ENABLE_HTTPS=false \
  --set PLATFORM_NAME="Open edX Instructor Dev"

Step 3: Create the Instructor plugin

bash
# Get the plugins directory
PLUGINS_DIR=$(tutor plugins printroot)
mkdir -p "$PLUGINS_DIR"

cat > "$PLUGINS_DIR/instructor.py" << 'EOF'
from tutor import hooks

hooks.Filters.ENV_PATCHES.add_item(
    (
        "openedx-lms-development-settings",
        """
CORS_ORIGIN_WHITELIST.append("http://apps.local.openedx.io:2003")
LOGIN_REDIRECT_WHITELIST.append("apps.local.openedx.io:2003")
CSRF_TRUSTED_ORIGINS.append("http://apps.local.openedx.io:2003")
INSTRUCTOR_MICROFRONTEND_URL = "http://apps.local.openedx.io:2003"
        """
    )
)

hooks.Filters.ENV_PATCHES.add_item(
    (
        "openedx-cms-development-settings",
        """
CORS_ORIGIN_WHITELIST.append("http://apps.local.openedx.io:2003")
LOGIN_REDIRECT_WHITELIST.append("apps.local.openedx.io:2003")
CSRF_TRUSTED_ORIGINS.append("http://apps.local.openedx.io:2003")
INSTRUCTOR_MICROFRONTEND_URL = "http://apps.local.openedx.io:2003"
        """
    )
)
EOF

Step 4: Configure devstack.py

bash
echo "INSTRUCTOR_MICROFRONTEND_URL = 'http://apps.local.openedx.io:2003'" \
  >> ~/openedx-instructor/openedx-platform/lms/envs/devstack.py

Step 5: Enable plugins and build image

bash
tutor plugins enable mfe
tutor plugins enable instructor
tutor config save
tutor images build openedx-dev

Step 6: Start containers

bash
tutor dev start -d

Step 7: Initialize the platform

This step creates databases, runs migrations, and sets up the platform. It may take several minutes.

bash
tutor dev do init

Step 8: Verify backend installation

bash
# Check all containers are running
docker ps --format "table {{.Names}}\t{{.Status}}" | grep tutor_dev

Expected output:

tutor_dev-lms-1           Up X minutes
tutor_dev-cms-1           Up X minutes
tutor_dev-mfe-1           Up X minutes
tutor_dev-mysql-1         Up X minutes
tutor_dev-mongodb-1       Up X minutes
tutor_dev-redis-1         Up X minutes

Step 9: Verify URLs are accessible

bash
# Check LMS is responding
curl -s -o /dev/null -w "%{http_code}" -H "Host: local.openedx.io" http://127.0.0.1:8000

# Check Studio is responding
curl -s -o /dev/null -w "%{http_code}" -H "Host: studio.local.openedx.io" http://127.0.0.1:8001

Expected output: 200 or 302 (redirect to login).

Step 10: Install and start the Instructor MFE

In a separate terminal:

bash
cd ~/openedx-instructor/frontend-app-instruct
npm install
npm run dev

The MFE starts on port 2003. The dev script is already configured for PORT=2003.


Creating Users

After installation, create users to access the platform.

Create an Admin User (Staff/Superuser)

bash
tutor dev do createuser --staff --superuser --password admin123 admin admin@example.com

Admin credentials:

  • Username: admin
  • Email: admin@example.com
  • Password: admin123
  • Permissions: Staff + Superuser (full admin access)

Create a Student User

bash
tutor dev do createuser --password student123 student student@example.com

Student credentials:

  • Username: student
  • Email: student@example.com
  • Password: student123
  • Permissions: Regular user (student)

Alternative: Manual user creation

If the createuser command fails:

bash
# Create admin
tutor dev run lms ./manage.py lms manage_user admin admin@example.com --staff --superuser
tutor dev run lms ./manage.py lms shell -c "
from django.contrib.auth import get_user_model
u = get_user_model().objects.get(username='admin')
u.set_password('admin123')
u.save()
"

# Create student
tutor dev run lms ./manage.py lms manage_user student student@example.com
tutor dev run lms ./manage.py lms shell -c "
from django.contrib.auth import get_user_model
u = get_user_model().objects.get(username='student')
u.set_password('student123')
u.save()
"

Access URLs

User TypeLogin URL
Adminhttp://local.openedx.io:8000/admin
All Usershttp://local.openedx.io:8000/login
Studio (Admin only)http://studio.local.openedx.io:8001

Accessing the Instructor MFE

Once the backend is running and the MFE is started, access instructor routes directly:

http://apps.local.openedx.io:2003/instructor/{COURSE_ID}/{TAB}

Example:

http://apps.local.openedx.io:2003/instructor/course-v1:Test+Test1+2025_T1/open_responses

To get a course ID, create a course in Studio and copy the ID from the course settings page.


Installing Without Affecting Existing Installations

1. Stop the existing instance first (to free ports)

bash
# If you have aliases configured
tutor-dev dev stop         # or tutor-instructor dev stop

# Or manually
source ~/existing-instance/.venv/bin/activate
export TUTOR_ROOT=~/existing-instance
tutor dev stop

2. Follow the installation steps above

This installation uses TUTOR_ROOT=~/openedx-instructor, which is completely separate from any other instance.

What stays isolated?

ResourceOther dev instanceThis instructor instance
ConfigurationOTHER_TUTOR_ROOT/config.yml~/openedx-instructor/config.yml
DatabaseOTHER_TUTOR_ROOT/data/mysql/~/openedx-instructor/data/mysql/
FilesOTHER_TUTOR_ROOT/data/~/openedx-instructor/data/
Python environmentOTHER/.venv/~/openedx-instructor/.venv/
Source codeSeparate clone~/openedx-instructor/openedx-platform/

Port conflict note: This environment uses ports 8000 and 8001 (same as other dev instances). Never run two dev instances simultaneously. Stop one before starting the other.


Managing Multiple Instances

Add these aliases to your ~/.bashrc or ~/.zshrc:

bash
# Instructor dev instance
alias tutor-instructor='source ~/openedx-instructor/.venv/bin/activate && TUTOR_ROOT=~/openedx-instructor tutor'

# General dev instance (if you have one)
alias tutor-dev='source ~/openedx-dev/.venv/bin/activate && TUTOR_ROOT=~/openedx-dev tutor'

# Local instance (if you have one)
alias tutor-local='source ~/tutor-local/.venv/bin/activate && TUTOR_ROOT=~/tutor-local tutor'

Then reload your shell:

bash
source ~/.zshrc   # or source ~/.bashrc

Usage:

bash
tutor-instructor dev start -d    # Start instructor backend
tutor-instructor dev stop        # Stop instructor backend

Starting the full Instructor environment

bash
# Terminal 1: Start backend
tutor-instructor dev start -d

# Terminal 2: Start MFE
cd ~/openedx-instructor/frontend-app-instruct
npm run dev

Troubleshooting

Error: "Could not verify sufficient RAM allocation in Docker"

Error message:

⚠️  Could not verify sufficient RAM allocation in Docker:
    Docker is configured to allocate 2048 MiB RAM, less than the recommended 4096 MiB
Tutor may not work if Docker is configured with < 4 GB RAM.
Aborted!
  1. Open Docker Desktop → Settings → Resources
  2. Set Memory to 4 GB or more
  3. Click Apply & Restart

Option B: Use non-interactive commands (Workaround)

Run the steps separately to bypass the RAM check:

bash
tutor config save
tutor plugins enable mfe
tutor plugins enable instructor
tutor config save
tutor images build openedx-dev
tutor dev start -d
tutor dev do init

Option C: Modify Tutor source (Advanced)

Since Tutor is installed in editable mode, you can disable the check:

  1. Edit ~/openedx-instructor/tutor/tutor/utils.py
  2. Find the function warn_macos_docker_memory()
  3. Comment out the validation:
    python
    def warn_macos_docker_memory() -> None:
        try:
            # check_macos_docker_memory()  # <-- Comment this line
            pass
        except exceptions.TutorError as e:
            # ... rest of the function

Error: "AmbiguousPluginError" after git pull

Error message:

xblock.plugin.AmbiguousPluginError

Cause: The Open edX package was renamed from Open_edX to openedx-platform.

Solution:

bash
docker exec tutor_dev-lms-1 pip uninstall --yes Open_edX
docker exec tutor_dev-lms-1 rm -rf /openedx/edx-platform/Open_edX.egg-info
docker exec tutor_dev-lms-1 pip install -e /openedx/edx-platform

tutor dev restart lms cms

MySQL authentication error after init

Error message:

django.db.utils.OperationalError: (1045, "Access denied for user 'openedx'@'...' (using password: YES)")

Solution: Stop and restart all services:

bash
tutor dev stop
tutor dev start -d

Wait a few seconds for MySQL to fully initialize.

Instructor MFE shows blank page or CORS errors

Symptoms: Browser console shows CORS or CSRF errors when accessing apps.local.openedx.io:2003.

Checklist:

  1. Verify the instructor plugin is enabled:
    bash
    tutor plugins list | grep instructor
  2. Verify the plugin was applied to the running config:
    bash
    tutor config printvalue LMS_HOST  # Should return local.openedx.io
  3. Rebuild the config and restart:
    bash
    tutor config save
    tutor dev restart lms cms
  4. Verify INSTRUCTOR_MICROFRONTEND_URL is in devstack.py:
    bash
    grep INSTRUCTOR_MICROFRONTEND_URL ~/openedx-instructor/openedx-platform/lms/envs/devstack.py

Instructor tab not visible in LMS

Symptom: The Instructor tab on the course page doesn't appear or doesn't redirect to the MFE.

Checklist:

  1. You must be logged in as a staff or superuser in a course where you have instructor access.
  2. The MFE must be running (npm run dev in a terminal).
  3. Verify INSTRUCTOR_MICROFRONTEND_URL is set in devstack.py (see above).
  4. Verify apps.local.openedx.io resolves in /etc/hosts.

Port already in use

Error: Bind for 0.0.0.0:8000 failed: port is already allocated

Solution:

  1. Check what's using the port: lsof -i :8000
  2. Stop any other running Tutor instance:
    bash
    tutor dev stop
    tutor local stop

Useful Commands

Backend Operations

bash
# Start backend (background)
tutor dev start -d

# Stop backend
tutor dev stop

# Restart backend
tutor dev restart

# Restart specific services
tutor dev restart lms cms

# View logs (all services)
tutor dev logs -f

# View logs (specific service)
tutor dev logs -f lms

MFE Operations

bash
# Start Instructor MFE
cd ~/openedx-instructor/frontend-app-instruct
npm run dev

# Build for production (if needed)
npm run build

Development Commands

bash
# Run bash in LMS container
tutor dev run lms bash

# Run Django shell
tutor dev run lms ./manage.py lms shell

# Run a Django management command
tutor dev run lms ./manage.py lms <command>

Configuration

bash
# Check current TUTOR_ROOT
tutor config printroot

# Check plugins directory
tutor plugins printroot

# List enabled plugins
tutor plugins list

# List mounts
tutor mounts list

Uninstallation

Remove this instance

bash
export TUTOR_ROOT=~/openedx-instructor

# Stop and remove containers
tutor dev dc down --remove-orphans

# Remove all data, config, and source code
rm -rf ~/openedx-instructor

Remove the instructor plugin (if no longer needed)

bash
PLUGINS_DIR=$(tutor plugins printroot)
rm "$PLUGINS_DIR/instructor.py"

Post-Installation Summary Template

For LLM assistants: After completing the installation, copy and fill in this template to provide the user with all necessary information:

markdown
## Installation Complete

Your Open edX Instructor development environment is now running.

### Access URLs

| Service | URL | Description |
|---------|-----|-------------|
| **LMS** | http://local.openedx.io:8000 | Main learning platform |
| **Studio** | http://studio.local.openedx.io:8001 | Course authoring tool |
| **Django Admin** | http://local.openedx.io:8000/admin | Backend administration |
| **Instructor MFE** | http://apps.local.openedx.io:2003 | Instructor Dashboard |

### User Credentials

#### Administrator
| Field | Value |
|-------|-------|
| Username | admin |
| Password | admin123 |
| Email | admin@example.com |
| Access | LMS, Studio, Django Admin, Instructor MFE |

#### Student
| Field | Value |
|-------|-------|
| Username | student |
| Password | student123 |
| Email | student@example.com |
| Access | LMS only |

### Managing the Platform

#### Start the backend
\`\`\`bash
source ~/openedx-instructor/.venv/bin/activate
export TUTOR_ROOT=~/openedx-instructor
tutor dev start -d
\`\`\`

#### Start the Instructor MFE (separate terminal)
\`\`\`bash
cd ~/openedx-instructor/frontend-app-instruct
npm run dev
\`\`\`

#### Stop the backend
\`\`\`bash
source ~/openedx-instructor/.venv/bin/activate
export TUTOR_ROOT=~/openedx-instructor
tutor dev stop
\`\`\`

### Accessing the Instructor Dashboard

1. Log in as admin at http://local.openedx.io:8000/login
2. Create or enroll in a course via Studio
3. Navigate to the Instructor tab in the course, or go directly to:
   `http://apps.local.openedx.io:2003/instructor/{COURSE_ID}/open_responses`

### Development Workflow

1. **Backend changes:** Edit files in `~/openedx-instructor/openedx-platform/` — Python changes auto-reload
2. **MFE changes:** Edit files in `~/openedx-instructor/frontend-app-instruct/` — Vite hot-reload applies automatically
3. Check backend logs if needed: `tutor dev logs -f lms`

Sources

Schema Education — Internal Research