Set Up Personal Learning Management System
If you're serious about structured learning—whether that's teaching yourself code, hosting courses for family, or building a knowledge hub—a self-hosted learning management system beats Udemy subscriptions and Coursera subscriptions by miles. I've been running Moodle on my homelab for eight months now, and it's become indispensable. You get full control over course content, student data stays private, and you can customize everything to your workflow.
Why Self-Host Your LMS?
Most people don't realize how much leverage a personal LMS gives you. You're not paying per-user licensing fees. You're not locked into someone else's UI. Your course data isn't being mined for behavioral analytics. When I needed to integrate custom quizzes tied to my own projects, or track learning outcomes for a small group, proprietary platforms either couldn't do it or wanted thousands per year.
A self-hosted system also scales with your ambition. Start with one course. Add fifty. The infrastructure cost stays the same. You control the entire student experience, from login to certification.
Option 1: Moodle on Docker (Full-Featured)
Moodle is the industry standard for a reason. It's battle-tested, supports plugins, gradebooks, forums, and real assessment. The downside: it's memory-hungry and needs MySQL or PostgreSQL. I prefer running it in Docker because you don't pollute your host system, and updates are seamless.
Here's my production Docker Compose stack. I run this on a VPS from RackNerd with 4GB RAM and 2 cores—more than enough for a small LMS:
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: moodle
POSTGRES_USER: moodleuser
POSTGRES_PASSWORD: change_me_securely
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U moodleuser"]
interval: 10s
timeout: 5s
retries: 5
moodle:
image: moodle:latest
depends_on:
postgres:
condition: service_healthy
ports:
- "8080:80"
environment:
MOODLE_DATABASE_TYPE: pgsql
MOODLE_DATABASE_HOST: postgres
MOODLE_DATABASE_USER: moodleuser
MOODLE_DATABASE_PASSWORD: change_me_securely
MOODLE_DATABASE_NAME: moodle
MOODLE_WWWROOT: https://learning.example.com
MOODLE_ADMIN_USER: admin
MOODLE_ADMIN_PASSWORD: secure_admin_pass
volumes:
- moodle_data:/var/www/html/moodledata
- moodle_html:/var/www/html
restart: unless-stopped
volumes:
postgres_data:
moodle_data:
moodle_html:
Deploy it with docker-compose up -d. Moodle will initialize the database on first run—this takes a few minutes. Once running, access it at http://localhost:8080 and log in with your admin credentials.
I route this through Caddy for HTTPS. My Caddyfile entry looks like:
learning.example.com {
reverse_proxy localhost:8080 {
header_up X-Forwarded-Proto https
header_up X-Forwarded-Host learning.example.com
}
}
Those X-Forwarded headers are critical. Without them, Moodle won't recognize it's behind HTTPS and redirects break. Speaking from painful experience here.
Option 2: Nextcloud with Learning Integration
If you already run Nextcloud for file storage and collaboration (and you probably should if you're self-hosting), you can bolt on learning features. It's lighter than Moodle and integrates seamlessly with your existing file structure.
Install the "Coursekit" or "Circles" apps from Nextcloud's app store, then create courses as collaborative projects. Students upload assignments directly to folders, get feedback via comments, and everything syncs to their devices.
The advantage: one authentication system, unified storage, less complexity. The disadvantage: fewer formal assessment tools and no dedicated gradebook. I use this for small group learning—teaching my kids programming, or helping friends learn DevOps concepts.
Building Your First Course
Once Moodle is running, here's the workflow I follow:
- Create a course (Categories > Courses > Add Course)
- Add sections (roughly equivalent to weeks or modules)
- Add learning activities: lectures (text + images), quizzes, assignments, forums
- Set enrollment keys or manual enrollment for students
- Configure the gradebook and final grade calculation
- Publish the course and enroll yourself first to test the student experience
For content, I prefer uploading a mix of PDF handouts, embedding YouTube videos (or self-hosted video via Jellyfin), and writing quiz questions directly in Moodle. Quizzes can be randomized per student, which discourages cheating and increases reuse.
A single course with 5 modules, 10 quizzes, and 50 enrolled students uses roughly 200MB of database space and serves fine on a 2GB RAM box. Scaling to hundreds of courses? You'd want 8GB+ and dedicated PostgreSQL tuning.
Backup and Data Integrity
This is non-negotiable. Your course data and student grades are irreplaceable. I backup my Moodle stack weekly:
- Database:
pg_dump -U moodleuser moodle > moodle_backup_$(date +%Y%m%d).sql - Uploaded files:
tar -czf moodledata_$(date +%Y%m%d).tar.gz /var/lib/docker/volumes/moodle_data/_data/ - Config: Save your docker-compose.yml and environment files
- Ship offsite: I use Restic to push encrypted backups to Wasabi cold storage
Test a restore once every few months. A backup you haven't tested is a paperweight.
Access and Networking
Running an LMS accessible only within your network is simpler, but less useful. I expose mine via Tailscale so I can reach it from anywhere without opening a firewall port. Family members get a Tailscale client, authenticate once, and they're in.
If you want public internet access, use Cloudflare Tunnel or a reverse proxy. Never expose port 8080 directly. Moodle isn't immune to attacks, and your database credentials are one SQL injection away from compromise.
I also enforce HTTPS everywhere and require two-factor authentication for admin accounts. Moodle supports TOTP (Google Authenticator) out of the box.
When to Upgrade Your Hardware
A single-core VPS with 1GB RAM will run Moodle but painfully. My baseline recommendation: 2GB RAM, 2 vCPUs, 20GB SSD for the initial setup. You'll comfortably handle 100 students and a few dozen courses.
RackNerd's KVM VPS plans are excellent value for this workload—you get burstable CPU and fixed RAM, which is exactly what you need. Their entry-level 2GB RAM plans run about $12/year and include unmetered bandwidth.
As you grow, monitor PostgreSQL query logs. Slow quiz loads or gradebook calculations usually point to missing indexes, not underpowered hardware. A properly tuned database on 2GB outperforms a bloated setup on 8GB.
Next Steps
Start small. Create one course on a test instance first. Import some free content from OpenStax or MIT OpenCourseWare to understand the UI. Then go deeper: add custom themes, integrate with your email system for notifications, or set up automated backups.
If you're not ready for full Moodle, Nextcloud is your safer bet. Both work brilliantly self-hosted, and both will serve you for years without paying subscription fees.