Self Host and Install Canvas LMS Ubuntu 24 Version

Post Author:

Adeel

Categories:

Date Posted:

May 18, 2026

Share This:

Self-host and Install Canvas LMS On Your Ubuntu 24 Server:

Canvas is a powerful open-source LMS that can be self-hosted on your own server. This is a detailed guide to help you Install Canvas LMS on Ubuntu 24.04 LTS using the Apache web server and enabling SSL for secure communication.

Caution: The steps below are fairly technical and should be performed by a server admin. The installation requires full access to the server this can be verified with the "sudo su" command. The requirements for the Canvas LMS when running all components on the same server are:

Want to See Canvas LMS in Action First?

Before you dive into the installation steps below, explore a live, fully working demo of Canvas LMS — see the dashboard, courses, and interface exactly as you'll get it after installation.


Try Live Demo →


RAM (Memory): 8 GB RAM

Processor: 4 CPU cores with 2.0 GHz or more

Disk Space: 40-50GB for storage

OS: Ubuntu 24.04 LTS - This is a MUST

Prerequisite Step:

Create a new user on your Linux system as "canvas". This user will have permission to run the Canvas LMS setup. It is recommended that you have a separate Linux user manage your canvas installation. After executing the below command it will ask you to set the password for this new user, make sure you keep the password handy as it will be used to login and confirm certain actions during the installation

sudo adduser canvas su - canvas

Step 1: Create a PostgreSQL User and Databases For Canvas

Once you execute this command, it will ask for your server password and then, your canvas PostgreSQL password. Please note the later one as it will be used when we will edit the canvas database config file.

sudo apt-get update -y && sudo apt-get install -y wget ca-certificates curl gnupg lsb-release && sudo install -d /usr/share/postgresql-common/pgdg && wget -qO - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo tee /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc && echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list && sudo apt-get update -y && sudo apt-get install -y postgresql-16 && sudo systemctl enable --now postgresql && sudo -u postgres createuser canvas --no-createdb --no-superuser --no-createrole --pwprompt && sudo -u postgres createdb canvas_production --owner=canvas && sudo -u postgres psql -d canvas_production -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"

Step 2: Installing Git, Ruby, Node.js, and Yarn

Install Ruby 3.4 as a native system package via the Instructure PPA, Node.js 20, and Yarn 1.22.22. Ruby 3.4 — Native System Install (Recommended)
Install Ruby 3.4 as a Native System Package. This is More Reliable Than Rbenv For
production servers because Apache/Passenger can access it directly without
needing shell profiles to be loaded.

sudo apt-get install -y git-core software-properties-common zlib1g-dev libxml2-dev libsqlite3-dev libpq-dev libxmlsec1-dev libidn-dev libyaml-dev curl make g++ && sudo add-apt-repository -y ppa:instructure/ruby && sudo apt-get update -y && sudo apt-get install -y ruby3.4 ruby3.4-dev && sudo update-alternatives --install /usr/bin/ruby ruby /usr/bin/ruby3.4 100 && sudo update-alternatives --install /usr/bin/gem gem /usr/bin/gem3.4 100 && ruby -v && curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash && source ~/.bashrc && nvm install 20 && nvm alias default 20 && node -v && npm install -g [email protected] && yarn -v

Step 3: Cloning and Install Canvas LMS

Always clone Canvas into /var/canvas. After cloning, initialize submodules and verify at least 50 gem directories are present before proceeding.

current_user=(whoami) && cd /var && sudo git clone https://github.com/instructure/canvas-lms.git canvas && sudo chown -R "current_user":"current_user" /var/canvas && cd /var/canvas && git checkout prod && for config in amazon_s3 database delayed_jobs vault_contents domain file_store outgoing_mail security external_migration cache_store redis session_store dynamic_settings; do cp config/config.yml.example config/$config.yml; done

Step 4: Configuring Database, Outgoing Mail and Domain Settings

Set your Database credentials in this step, keep everything as it is, and just set the password to the value you entered in Step 1;

Note: When you open a file with  nano command then press ctrl + x then Y then Enter to save the changes to the file.

In the next steps, we will use this placeholder .  Make sure you replace this with your actual domain name used for Canvas before executing the commands.

 

nano config/database.yml

Open database.yml and keep only the production block with these settings:

production:
adapter: postgresql
encoding: utf8
database: canvas_production username: canvas password:{your_password_from_step_1}
host: localhost

Set the dynamic settings correctly for LTI and Rich Content Editor integrations.

nano config/dynamic_settings.yml

Make sure you replace development with production at the top:



production:

 config:

  canvas:

    canvas:

     encryption-secret: {random_32_char_string}

     signing-secret: {random_32_char_string}

 rich-content-service:

  app-host: https://your-domain.com


Set your SMTP mail server details for emails to work on your Canvas LMS.



nano config/outgoing_mail.yml



These settings are tested to work with Gmail App Passwords on port 465:



production:
address: smtp.gmail.com # Your SMTP server address
port: "465" # Port 465 for SSL
enable_starttls_auto: false # Disable TLS
ssl: true # Enable SSL
user_name: "{Your_SMTP_OR_GMAIL_USERNAME}"
password: "{Your_SMTP_OR_GMAIL_PASSWORD}"
authentication: plain # secure authentication
domain: smtp.gmail.com # Your SMTP server address
outgoing_address: "{YOUR_EMAIL}"
default_name: "{YOUR_FROM_NAME_ON_EMAILS}"



If using port 587 (TLS), use:


production:
address: smtp.gmail.com
port: "587" # Port 587 for TLS
enable_starttls_auto: true
ssl: false
user_name: "{Your_SMTP_OR_GMAIL_USERNAME}"
password: "{Your_SMTP_OR_GMAIL_PASSWORD}"
authentication: plain
domain: smtp.gmail.com
outgoing_address: "{YOUR_EMAIL}"
default_name: "{YOUR_FROM_NAME_ON_EMAILS}"



Set your domain name under Production -> domain.



nano config/domain.yml




production:
  domain: your-domain.com
  files_domain: your-domain.com
  ssl: true



Insert a randomized string of at least 20 characters in encryption_key and set your domain in lti_iss. This is required for LTI tools.



nano config/file_store.yml




production:
  storage: local
  path_prefix: tmp/files



Set session store configuration:



nano config/session_store.yml




production:
  session_store: cache_store
  expire_after: 86400



Set Redis connection configuration:



nano config/redis.yml




production:
 url:
   - redis://localhost



Set cache store:



nano config/cache_store.yml




production:
   cache_store: redis_cache_store



Step 5: Installing Dependencies and Compiling Assets

If your server has under ~8GB total memory, add swap first:



sudo fallocate -l 4G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile && echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Then install gems, JS deps, initialize the database, and compile assets:


cd /var/canvas && sudo apt-get install -y libyaml-dev && sudo gem install bundler --version 2.5.10 --no-document && bundle config set --local path vendor/bundle && bundle install && yarn install && yarn gulp rev && export [email protected] && export CANVAS_LMS_ADMIN_PASSWORD=your_secure_password && export CANVAS_LMS_ACCOUNT_NAME=Your_Account_Name && export CANVAS_LMS_STATS_COLLECTION=opt_out && RAILS_ENV=production bundle exec rake db:initial_setup && RAILS_ENV=production bundle exec rake db:migrate && mkdir -p log tmp/pids public/assets app/stylesheets/brandable_css_brands && touch app/stylesheets/_brandable_variables_defaults_autogenerated.scss Gemfile.lock log/production.log && NODE_OPTIONS='--max-old-space-size=4096' RAILS_ENV=production bundle exec rake canvas:compile_assets_dev || NODE_OPTIONS='--max-old-space-size=4096' RAILS_ENV=production bundle exec rake canvas:compile_assets && RAILS_ENV=production bundle exec rake brand_configs:generate_and_upload_all

Step 6: Installing and Configuring Apache

Important: Install Passenger as a gem. Do not use libapache2-mod-passenger (older package versions can break Canvas logins).



sudo apt-get install -y apache2 apache2-dev libcurl4-openssl-dev libssl-dev zlib1g-dev g++ make libapache2-mod-xsendfile && sudo gem install passenger --no-document && sudo passenger-install-apache2-module --auto --languages ruby && sudo passenger-install-apache2-module --snippet | grep '^LoadModule' | sudo tee /etc/apache2/mods-available/passenger.load && sudo bash -c 'cat > /etc/apache2/mods-available/passenger.conf <

PassengerRoot $(passenger-config --root)

PassengerDefaultRuby /usr/bin/ruby3.4

PassengerDefaultUser canvas

PassengerStartTimeout 180

PassengerPreloadBundler On

PassengerFriendlyErrorPages Off

PassengerInstanceRegistryDir /var/run/passenger-instreg



EOF' && sudo mkdir -p /var/run/passenger-instreg && sudo chmod 0755 /var/run/passenger-instreg && sudo a2enmod rewrite passenger ssl headers proxy proxy_http xsendfile && sudo apache2ctl configtest && sudo systemctl restart apache2



Step 7: Obtain SSL Certificate For Your Domain

Canvas requires a valid & verified SSL certificate to be installed for your domain, please note that self signed cert will not work. We will use Lets Encrypt to get a free certificate and make it auto-renew so it does not expire after the 3-month period.

A) Install Certbot:



sudo apt update && sudo apt install -y certbot python3-certbot-apache


B) Obtain the SSL Certificate:

DNS for must already point to this server, and port 80 must be open.



sudo certbot --apache -d {your_domain}


Certificate paths:

Cert: /etc/letsencrypt/live//fullchain.pem
Key: /etc/letsencrypt/live//privkey.pem

C) Automatic Renewal:

Ubuntu’s certbot package installs a systemd timer. Verify with:



sudo certbot renew --dry-run


Step 8: Installing and Configuring Apache+Passenger

First, disable default Apache sites:



sudo a2dissite 000-default.conf default-ssl.conf 000-default-le-ssl.conf 2>/dev/null || true && sudo rm -f /etc/apache2/sites-enabled/000-default.conf /etc/apache2/sites-enabled/default-ssl.conf /etc/apache2/sites-enabled/000-default-le-ssl.conf


Create the HTTP vhost:



sudo nano /etc/apache2/sites-available/canvas.conf




<VirtualHost *:80>
ServerName {your_domain}

DocumentRoot /var/canvas/public

PassengerAppRoot /var/canvas

PassengerRuby /usr/bin/ruby3.4

PassengerAppEnv production

RailsEnv production

PassengerEnabled on

<Directory /var/canvas/public>

AllowOverride all

Options -MultiViews

Require all granted

</Directory>

</VirtualHost>


Create the HTTPS vhost:



sudo nano /etc/apache2/sites-available/canvas-ssl.conf




<IfModule mod_ssl.c>
<VirtualHost *:443>

ServerName {your_domain}

DocumentRoot /var/canvas/public

PassengerAppRoot /var/canvas

PassengerRuby /usr/bin/ruby3.4

PassengerAppEnv production

RailsEnv production

PassengerEnabled on

LimitRequestBody 0

Timeout 3600

ProxyTimeout 3600

RequestHeader set X-Forwarded-Proto "https" early

RequestHeader set X-Forwarded-SSL "on" early

SSLEngine On

SSLCertificateFile /etc/letsencrypt/live/{your_domain}/fullchain.pem

SSLCertificateKeyFile /etc/letsencrypt/live/{your_domain}/privkey.pem

Include /etc/letsencrypt/options-ssl-apache.conf

<Directory /var/canvas/public>

AllowOverride all

Options -MultiViews

Require all granted

</Directory>

XSendFile On

XSendFilePath /var/canvas

ProxyPassMatch ^/api/(session|documents|folders|files|file|images|upload|usage_rights|flickr_search|youtube_title|wikiPages|modules|assignments|announcements|discussions|quizzes|rceConfig|media_objects|media_attachments)(/.*)?$ http://127.0.0.1:3001/api/$1$2

ProxyPassReverse /api/session http://127.0.0.1:3001/api/session

ProxyPassReverse /api/documents http://127.0.0.1:3001/api/documents

ProxyPassReverse /api/folders http://127.0.0.1:3001/api/folders

ProxyPassReverse /api/files http://127.0.0.1:3001/api/files

ProxyPassReverse /api/images http://127.0.0.1:3001/api/images

</VirtualHost>

</IfModule>

Enable the sites:



sudo a2ensite canvas.conf && sudo a2ensite canvas-ssl.conf && sudo apache2ctl configtest && sudo systemctl restart apache2


Step 9: Setup Automated jobs & Firewall Rules

Create a systemd service for Canvas background jobs:



sudo tee /etc/systemd/system/canvas-jobs.service > /dev/null <<'EOF'[Unit] Description=Canvas LMS background job workers (delayed_job)

After=network.target postgresql.service redis-server.service

[Service] Type=forking

User=canvas

WorkingDirectory=/var/canvas

Environment=RAILS_ENV=production

ExecStart=/var/canvas/script/delayed_job start

ExecStop=/var/canvas/script/delayed_job stop

PIDFile=/var/canvas/tmp/pids/delayed_jobs_pool.pid

Restart=on-failure

RestartSec=10

[Install] WantedBy=multi-user.target

EOF

sudo mkdir -p /var/canvas/tmp/pids && sudo chown -R canvas:canvas /var/canvas/tmp/pids && sudo systemctl daemon-reload && sudo systemctl enable --now canvas-jobs && sudo systemctl status canvas-jobs --no-pager


Firewall (only open what is needed publicly):



sudo ufw allow OpenSSH && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw --force enable && sudo ufw reload && sudo ufw status


Step 10: Setup Redis in Cache Configuration

Some Canvas features require Redis (including OAuth2 / LTI). On Ubuntu 24.04, install Redis from the official Ubuntu packages



sudo apt-get install -y redis-server && sudo systemctl enable --now redis-server && redis-cli ping



Confirm /var/canvas/config/cache_store.yml contains:



production:

   cache_store: redis_cache_store


Confirm /var/canvas/config/redis.yml contains:



production:
url:

  - redis://localhost




sudo systemctl restart redis-server


Step 11: Enable Canvas Rich Content Editor

Canvas requires the Canvas RCE API to be running for the rich content editor. Use PM2 so it survives reboots. Do not run RCE in a screen session.
Clone and install RCE (as the canvas user):



cd /home/canvas && git clone https://github.com/instructure/canvas-rce-api.git && cd /home/canvas/canvas-rce-api && python3 - <<'PY'

import json, pathlib, re

root = pathlib.Path('.')

pkg = json.loads((root/'package.json').read_text())

ver = pkg.get('devDependencies', {}).pop('dev-null', None)

pkg.setdefault('dependencies', {})

pkg['dependencies']['dev-null'] = ver or '0.1.1'

(root/'package.json').write_text(json.dumps(pkg, indent=2)+'n')

rl = root/'app'/'middleware'/'requestLogs.js'

if rl.exists():

t = rl.read_text()

t2 = re.sub(r"^imports+devNulls+froms+['"]dev-null['"];s*n", "", t, count=1, flags=re.M)

t2 = t2.replace('return devNull();', 'return { write() {} };')

rl.write_text(t2)

print('dev-null dependency fixed')

PY

npm install --omit=dev && npm install --save-prod [email protected]


Create the RCE .env file:


ECOSYSTEM_SECRET=$(head -c 32 /dev/urandom | base64 | tr -d '+/=' | tr -dc 'a-zA-Z0-9' | head -c 32)
&& ECOSYSTEM_KEY=$(head -c 32 /dev/urandom | base64 | tr -d '+/=' | tr -dc 'a-zA-Z0-9' | head -c 32)
&& CIPHER_PASSWORD=$(openssl rand -hex 16)
&& echo "ECOSYSTEM_KEY=$ECOSYSTEM_KEY"
&& echo "ECOSYSTEM_SECRET=$ECOSYSTEM_SECRET"
&& printf 'PORT=3001nNODE_ENV=productionnSTATSD_HOST=127.0.0.1nSTATSD_PORT=8125nSTATS_PREFIX=rceapinCIPHER_PASSWORD=%snECOSYSTEM_KEY=%snECOSYSTEM_SECRET=%sn' "$CIPHER_PASSWORD" "$ECOSYSTEM_KEY" "$ECOSYSTEM_SECRET" > /home/canvas/canvas-rce-api/.env
&& chmod 600 /home/canvas/canvas-rce-api/.env

Update Canvas secrets to match (use the printed ECOSYSTEM_KEY / ECOSYSTEM_SECRET values):



nano /var/canvas/config/vault_contents.yml




production:
'app-canvas/data/secrets':
data:
canvas_security:
encryption_secret: "YOUR_ECOSYSTEM_KEY"
signing_secret: "YOUR_ECOSYSTEM_SECRET"





nano /var/canvas/config/dynamic_settings.yml




production:
'app-canvas/data/secrets':
data:
canvas_security:
encryption_secret: "YOUR_ECOSYSTEM_KEY"
signing_secret: "YOUR_ECOSYSTEM_SECRET"
rich-content-service:
  app-host: https://{your_domain}



IMPORTANT: Do not use tabs in YAML files. Keep indentation exact.

Start RCE with PM2:



cd /home/canvas/canvas-rce-api && npm install -g pm2 && pm2 start npm --name canvas-rce-api -- start && pm2 status && pm2 startup


Copy and run the command printed by pm2 startup, then:



pm2 save && curl http://127.0.0.1:3001 && sudo systemctl restart apache2


We know it’s complex—let us help!

Canvas Installation Service

Step 12: Set Correct Permissions & ensure users can't read private Canvas files



cd /var/canvas && current_user=(whoami) && sudo chown -R "current_user":"$current_user" . && sudo find config/ -type f -exec chmod 400 {} + && sudo systemctl restart apache2 canvas-jobs


Step 13: Optimizing File Downloads (Optional)



sudo apt-get install -y libapache2-mod-xsendfile && sudo systemctl reload apache2 && nano /var/canvas/config/environments/production-local.rb


Add:



Rails.application.configure do config.action_dispatch.x_sendfile_header = 'X-Sendfile' end




sudo systemctl restart apache2


Final Verification



sudo systemctl is-active apache2 && sudo systemctl is-active postgresql && redis-cli ping && sudo systemctl is-active canvas-jobs && pm2 status && curl -I https://{your_domain}/login && curl http://127.0.0.1:3001



You should see Apache/PostgreSQL/jobs active, Redis PONG, RCE online, and Canvas login returning HTTP 200/302 with Passenger.

Conclusion: Canvas LMS Installed

By following this step-by-step guide, you can Install Canvas LMS on your own Ubuntu 24.04 Server. Please be advised that these configurations need to be handled by a server administrator.

We know it's quite complex to perform the installation so we are here to help with that as well.

Canvas LMS Installation Price

We have a one-time setup fee for setting up Canvas LMS on your own server. There are some prerequisites to allow us to begin the installation process for you. Make sure you share with us the below items before you purchase the install package.

One Time Installation Service

Basic Canvas LMS Setup

$350*
Pre-Requisites for Installation
  • A fresh server running Ubuntu version -> 22.04
  • Port 3000 must be accessible on the server for the RCE setup
  • A minimum of 8 GB of RAM is recommended for Canvas LMS.
  • Root access to the server is needed.
  •  A domain URL pointing to your Linux server.
  •  SMTP server credentials (your email and app password) to enable emails from Canvas. Follow this guide till Step #3 to generate the app password after enabling 2-step verification for your Google account.
what you’ll get
  • Core Canvas LMS installation
  • Email Setup

  • System Firewall Setup

  • SSL Certificate/HTTPS Setup

  • Rich Content Editor Functionality

  • Redis Cache Configuration

  • This package includes everything you need to set up the basic open-source version of Canvas LMS software on your own server

Package does not include: If there are any other setup or installation tasks apart from the core Canvas LMS setup then it would be charged separately based on the hourly rates mentioned here.

Install Completion Time: Approx 2 working days

*Package is subject to our development service terms and conditions

Having trouble deciding how to proceed? reach out to us below and we’ll take you through!

Let’s discuss your project!

    Our Recommendation

    Affiliate Disclosure: We use affiliate links in our content. It wouldn’t cost you anything. However, it helps us offset the cost of producing the content and the offerings. Thanks for your support.

    About the Author: Adeel

    A decade in web development turned into a focus on progressive education models and eLearning tools. I express that through code: Adaptive Learning for LearnDash, Virtual Classroom for WordPress, and WPZoomy, to name a few.

    recent posts