Self Host and Install Canvas LMS Ubuntu 24 Version
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.
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 - canvasStep 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 -vStep 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; doneStep 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.ymlOpen 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: localhostSet the dynamic settings correctly for LTI and Rich Content Editor integrations.
nano config/dynamic_settings.ymlMake 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}"
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
Need Our Help?
Canvas Installation ServiceStep 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 ServiceStep 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
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
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!
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.







