
This article provides a guide demonstrating how to install Canvas LMS on Ubuntu VPS.
What is Canvas LMS?
Canvas LMS is a powerful open-source Learning Management System developed by Instructure. It provides course management, assignments, grading, discussions, quizzes, user enrollment, integrations, APIs, and many of the features commonly required by schools, universities, training organizations, and corporate learning environments.
Although Instructure offers Canvas as a hosted commercial service, the open-source edition can be installed on your own Ubuntu VPS.
This guide walks through deploying a production-ready Canvas LMS server on Ubuntu 22.04 LTS, including:
- PostgreSQL
- Ruby 3.4
- Node.js 20
- Yarn
- Redis
- Canvas LMS
- Apache
- Phusion Passenger
- Background workers
- HTTPS using Let’s Encrypt
- SMTP configuration
- Basic production hardening
Canvas is considerably more resource-intensive than a typical WordPress or small Rails application. Instructure recommends at least 8 GB of RAM when the web application, database, Redis, and background workers are running on the same machine.
For a small production installation, a sensible starting VPS configuration is:
| Resource | Recommended |
|---|---|
| CPU | 4 vCPU or more |
| RAM | 16 GB minimum |
| Storage | 256 GB SSD/NVMe |
| OS | Ubuntu 22.04 LTS |
| Database | PostgreSQL 14+ |
| Redis | Redis 6+ |
| Ruby | Ruby 3.4.1+ |
| Node.js | Node.js 20+ |
For larger deployments, PostgreSQL, Redis, Canvas web nodes, background workers, and object storage should eventually be separated.
Compare Ubuntu VPS Plans
How to Install Canvas LMS on Ubuntu VPS
To install Canvas LMS on Ubuntu VPS, follow the steps below:
1. Prepare the DNS Records
Before installing Canvas, create DNS records pointing to your VPS.
For example:
canvas.example.com A 192.0.2.10 canvasfiles.example.com A 192.0.2.10
Replace 192.0.2.10 with the public IP address of your VPS.
The second hostname is important because Canvas recommends serving user-uploaded content from a hostname separate from the main Canvas application. Instructure specifically notes that files_domain should be a different hostname from the browser’s perspective, even if both hostnames ultimately point to the same web server.
Throughout this guide, replace:
canvas.example.com
with your actual Canvas hostname.
2. Connect to the Ubuntu VPS
ssh root@SERVER_IP
Update the server:
apt update apt upgrade -y
Install some basic utilities:
apt install -y \
curl \
wget \
git \
nano \
unzip \
ca-certificates \
gnupg \
software-properties-common \
build-essential
Set the hostname if necessary:
hostnamectl set-hostname canvas.example.com
Confirm:
See Also: How to Install Dokploy on Ubuntu VPS
hostnamectl
3. Create an Administrative User
Although you can initially configure the server as root, it is better to create a normal sudo-enabled administrator.
For example:
adduser canvasadmin
Add the account to the sudo group:
usermod -aG sudo canvasadmin
Switch to it:
su - canvasadmin
For the remainder of the guide, commands requiring elevated privileges use sudo.
4. Configure the Firewall
If UFW is enabled, allow SSH, HTTP, and HTTPS:
sudo ufw allow OpenSSH sudo ufw allow 80/tcp sudo ufw allow 443/tcp
Enable UFW:
sudo ufw enable
Check the rules:
sudo ufw status
You should see ports 22, 80, and 443 allowed.
Do not expose PostgreSQL or Redis publicly when they are installed locally.
5. Install PostgreSQL
Canvas uses PostgreSQL for its production database and currently requires PostgreSQL 14 or newer.
Ubuntu 22.04 provides PostgreSQL 14:
sudo apt install -y postgresql postgresql-contrib libpq-dev
Start and enable PostgreSQL:
sudo systemctl enable --now postgresql
Verify:
sudo systemctl status postgresql
Check its version:
psql --version
You should have PostgreSQL 14 or newer.
6. Create the Canvas PostgreSQL User and Database
Create a dedicated PostgreSQL account:
sudo -u postgres createuser canvas \
--no-createdb \
--no-superuser \
--no-createrole \
--pwprompt
You will be prompted to create a database password.
Use a long random password and save it securely.
For example:
YOUR_STRONG_DATABASE_PASSWORD
Create the Canvas production database:
sudo -u postgres createdb canvas_production --owner=canvas
Verify:
sudo -u postgres psql -c "\l"
You should see:
canvas_production
owned by:
canvas
Test the database login:
psql -h localhost -U canvas -d canvas_production
Enter the database password.
If successful, quit PostgreSQL with:
\q
7. Install Redis
Redis is strongly recommended by Canvas and is required by some Canvas functionality, including OAuth-related functionality. The current production documentation specifies Redis 6.x or later.
Install Redis:
sudo apt install -y redis-server
Enable it:
sudo systemctl enable --now redis-server
Test Redis:
redis-cli ping
Expected response:
PONG
Check the version:
redis-server --version
For a single-server installation, Redis should remain bound to localhost.
Check:
sudo ss -lntp | grep 6379
You generally want Redis listening only on:
127.0.0.1:6379
or another private/internal address.
8. Install Canvas Ruby Dependencies
Current Canvas releases require at least Ruby 3.4.1. Ruby 3.5+ is currently described by the Canvas production documentation as untested.
Instructure maintains an Ubuntu Ruby PPA.
Add it:
sudo add-apt-repository -y ppa:instructure/ruby sudo apt update
Install Ruby and the required development libraries:
See Also: 7 Steps to Easily Configure OpenLiteSpeed as a Reverse Proxy for Metabase
sudo apt install -y \
ruby3.4 \
ruby3.4-dev \
zlib1g-dev \
libxml2-dev \
libsqlite3-dev \
libpq-dev \
libxmlsec1-dev \
libyaml-dev \
libidn11-dev \
curl \
make \
g++
These dependencies closely follow Instructure’s current production installation recommendations.
Check Ruby:
ruby --version
You want something similar to:
ruby 3.4.x
If Ubuntu still points ruby at another version, inspect:
which ruby ruby --version
9. Install Node.js 20
Canvas currently requires Node.js 20 or newer, and the current source tree specifies Node.js >=20.0.0. The repository’s .nvmrc currently points to Node 20.9.0.
Install Node.js 20 from NodeSource:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
Install Node:
sudo apt install -y nodejs
Check it:
node --version npm --version
You should see Node 20.x or newer.
10. Install Yarn
Canvas continues to use Yarn 1.x for its JavaScript dependency tree. The current package configuration specifies Yarn ^1.19.1.
Install Yarn:
sudo npm install -g yarn
Check:
yarn --version
A Yarn 1.x release should be returned.
Do not use Yarn Berry/Yarn 2+ unless the Canvas source branch explicitly changes its package requirements.
11. Create the Canvas Runtime User
Create a dedicated system account that will run Canvas:
sudo adduser \
--disabled-password \
--gecos "" \
canvasuser
Canvas will ultimately run under this account instead of root.
12. Download Canvas LMS
Move to a temporary working directory:
cd ~
Clone the official repository:
git clone https://github.com/instructure/canvas-lms.git
Enter it:
cd canvas-lms
For a production installation, use Instructure’s production branch:
git checkout prod
The official Production Start documentation currently recommends the prod branch for production deployments.
Confirm:
git branch --show-current
Expected:
prod
13. Move Canvas to /var/canvas
Create the production application directory:
sudo mkdir -p /var/canvas
Give your administrative user temporary ownership:
sudo chown -R "$USER":"$USER" /var/canvas
Copy the Canvas application:
cp -a ~/canvas-lms/. /var/canvas/
Enter the application directory:
cd /var/canvas
Confirm:
ls
You should see directories such as:
app config db doc gems lib public script ui
along with:
Gemfile Rakefile package.json
14. Install Bundler
Canvas uses Bundler to manage its Ruby dependencies.
The current Canvas lock file identifies Bundler 2.6.7, so installing that version avoids unnecessary dependency-resolution differences.
Install Bundler:
sudo gem install bundler -v 2.6.7
Verify:
bundle --version
Configure dependencies to install underneath the Canvas tree:
bundle config set --local path vendor/bundle
Then install the gems:
See Also: 🚀 How to Deploy Apache JMeter on Ubuntu VPS
bundle install
This can take several minutes and consume substantial CPU and RAM.
If compilation fails, review the error carefully. Most gem failures indicate a missing development library rather than a Canvas problem.
15. Install Canvas JavaScript Dependencies
From /var/canvas:
yarn install
This downloads a substantial dependency tree.
Verify it completes without errors.
If Yarn complains about Node compatibility, check:
node --version
and:
cat package.json | grep -A3 '"engines"'
Do not bypass engine checks unless you have a very specific reason to do so.
16. Create Canvas Configuration Files
Canvas ships example configuration files that must be copied before initialization.
Run:
cd /var/canvas
Then:
for config in amazon_s3 database vault_contents delayed_jobs domain file_store outgoing_mail security external_migration; do
cp config/${config}.yml.example config/${config}.yml
done
Also create the dynamic settings file:
cp config/dynamic_settings.yml.example config/dynamic_settings.yml
17. Configure the Database
Edit:
nano config/database.yml
Find the production section and configure it similar to:
production: adapter: postgresql encoding: utf8 database: canvas_production host: localhost username: canvas password: "YOUR_STRONG_DATABASE_PASSWORD" timeout: 5000
Save the file.
Test the credentials independently if needed:
PGPASSWORD='YOUR_STRONG_DATABASE_PASSWORD' \ psql -h localhost -U canvas -d canvas_production -c "SELECT version();"
If PostgreSQL responds, Canvas should be able to connect as well.
18. Configure the Canvas Domain
Edit:
nano config/domain.yml
Configure the production section:
production: domain: "canvas.example.com" ssl: true files_domain: "canvasfiles.example.com"
The important values are:
domain files_domain ssl
Use only hostnames here; do not add:
https://
before the domain unless the particular setting explicitly calls for a URL.
19. Configure Canvas Security Secrets
Copying the example already created:
config/security.yml
Edit it:
nano config/security.yml
Canvas requires randomized secret values. The official documentation requires random strings of at least 20 characters.
Generate strong secrets with:
openssl rand -hex 64
Run the command separately for each required secret.
For example:
openssl rand -hex 64 openssl rand -hex 64 openssl rand -hex 64
Place the generated strings into the corresponding production values in security.yml.
Never reuse the example secrets in production.
Never publish this file.
20. Configure Redis
Create the Redis configuration:
cp config/redis.yml.example config/redis.yml
Edit it:
nano config/redis.yml
Configure:
production:
url:
- redis://127.0.0.1:6379/0
Now configure Canvas caching:
See Also: 🚀 How to Install and Run Rocket.Chat on Debian VPS
cp config/cache_store.yml.example config/cache_store.yml
Edit:
nano config/cache_store.yml
Use:
production: cache_store: redis_cache_store
This matches the Redis cache configuration recommended in the current Canvas Production Start documentation.
21. Configure Outgoing Email
Canvas relies heavily on email for notifications, invitations, password resets, announcements, and course activity.
Edit:
nano config/outgoing_mail.yml
A typical authenticated SMTP configuration resembles:
production: address: "smtp.example.com" port: "587" user_name: "canvas@example.com" password: "YOUR_SMTP_PASSWORD" authentication: "plain" enable_starttls_auto: true domain: "example.com" outgoing_address: "canvas@example.com" default_name: "Canvas LMS"
Replace these settings with your SMTP provider’s actual information.
The domain and outgoing_address fields describe Canvas’s outbound identity, while the SMTP address, credentials, and port describe the mail server connection.
For production email deliverability, also configure:
- SPF
- DKIM
- DMARC
- valid PTR/rDNS where applicable
22. Prepare Canvas Runtime Directories
Create required directories:
cd /var/canvas
Run:
mkdir -p \
log \
tmp/pids \
public/assets \
app/stylesheets/brandable_css_brands
Create required files:
touch app/stylesheets/_brandable_variables_defaults_autogenerated.scss touch log/production.log
23. Initialize the Canvas Database
Before running initialization, generate the asset revision:
yarn gulp rev
Now run the initial Canvas database setup:
RAILS_ENV=production bundle exec rake db:initial_setup
Canvas will prompt for information including:
Admin email Admin password Organization/account name Usage statistics preference
For example:
Admin email: admin@example.com Account name: Example University
Use a strong administrator password.
Canvas also supports environment variables for automating these answers:
CANVAS_LMS_ADMIN_EMAIL CANVAS_LMS_ADMIN_PASSWORD CANVAS_LMS_ACCOUNT_NAME CANVAS_LMS_STATS_COLLECTION
The official installer supports opt_in, opt_out, or anonymized for statistics collection.
After initialization, your Canvas database should contain the required schema and initial administrator account.
24. Compile Canvas Production Assets
Canvas must build its CSS, JavaScript, branding, and other static assets before the interface will work correctly.
Run:
cd /var/canvas
Then:
RAILS_ENV=production \ SASS_STYLE=compressed \ bundle exec rake canvas:compile_assets
Asset compilation is one of the most resource-intensive stages of the installation.
On a smaller VPS, compilation may fail because of memory exhaustion.
If necessary, temporarily create swap:
sudo fallocate -l 8G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile
Verify:
free -h
You can also reduce Canvas build concurrency:
See Also: 🚀 How to Deploy Coolify on Ubuntu VPS
export CANVAS_BUILD_CONCURRENCY=1
Then rerun the asset compilation.
Recent Canvas deployment experience confirms that missing or failed asset compilation can leave the Rails application technically accessible while the UI is incomplete, broken, or unstyled.
25. Set Canvas File Ownership
Set the Canvas runtime user as the owner of files it must modify:
sudo chown -R canvasuser:canvasuser \
/var/canvas/log \
/var/canvas/tmp \
/var/canvas/public/assets \
/var/canvas/public/dist \
/var/canvas/app/stylesheets/brandable_css_brands
Also:
sudo chown canvasuser:canvasuser \
/var/canvas/config/environment.rb \
/var/canvas/config.ru \
/var/canvas/app/stylesheets/_brandable_variables_defaults_autogenerated.scss
Protect sensitive configuration files:
sudo chown canvasuser:canvasuser /var/canvas/config/*.yml sudo chmod 400 /var/canvas/config/*.yml
Canvas’s production documentation specifically recommends restricting these YAML files because they can contain database passwords, SMTP credentials, encryption keys, and other secrets.
26. Install Apache
Canvas’s official production deployment guide uses Apache + Phusion Passenger.
Install Apache:
sudo apt install -y apache2
Enable common required modules:
sudo a2enmod rewrite sudo a2enmod ssl sudo a2enmod headers
27. Install Phusion Passenger
Install the repository prerequisites:
sudo apt install -y \
dirmngr \
gnupg \
apt-transport-https \
ca-certificates \
curl
Install Phusion’s current signing key:
curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key-2025.txt \
| gpg --dearmor \
| sudo tee /etc/apt/trusted.gpg.d/phusion.gpg >/dev/null
Add the Ubuntu 22.04/Jammy Passenger repository:
echo "deb https://oss-binaries.phusionpassenger.com/apt/passenger jammy main" \
| sudo tee /etc/apt/sources.list.d/passenger.list
Update:
sudo apt update
Install Passenger’s Apache module:
sudo apt install -y libapache2-mod-passenger
Enable Passenger:
sudo a2enmod passenger
Restart Apache:
sudo systemctl restart apache2
These commands use Phusion’s current repository/key installation method rather than the older deprecated apt-key workflow.
Validate Passenger:
sudo passenger-config validate-install
All checks should pass.
28. Tell Passenger Which Ruby to Use
Determine Ruby’s location:
which ruby
For example:
/usr/bin/ruby
Inspect Passenger configuration:
sudo nano /etc/apache2/mods-available/passenger.conf
Ensure Passenger is using the correct Ruby.
For example:
PassengerRuby /usr/bin/ruby PassengerDefaultUser canvasuser
Verify Ruby:
/usr/bin/ruby --version
Make sure it reports Ruby 3.4.x.
Restart Apache:
sudo systemctl restart apache2
29. Create the Canvas Apache VirtualHost
Disable the default Apache site:
sudo a2dissite 000-default.conf
Create:
sudo nano /etc/apache2/sites-available/canvas.conf
Initially configure HTTP:
ServerName canvas.example.com
ServerAlias canvasfiles.example.com
ServerAdmin admin@example.com
DocumentRoot /var/canvas/public
SetEnv RAILS_ENV production
PassengerAppEnv production
PassengerDefaultUser canvasuser
Options FollowSymLinks
AllowOverride All
Require all granted
ErrorLog ${APACHE_LOG_DIR}/canvas_error.log
CustomLog ${APACHE_LOG_DIR}/canvas_access.log combined
Enable it:
See Also: Easily Deploy NodeBB Community Forum on Ubuntu VPS
sudo a2ensite canvas.conf
Check the configuration:
sudo apachectl configtest
Expected:
Syntax OK
Restart Apache:
sudo systemctl restart apache2
30. Test Canvas Before Adding SSL
Check Apache:
sudo systemctl status apache2
Check Canvas:
curl -I http://canvas.example.com/
Also check:
curl -I http://canvas.example.com/health_check
If Canvas starts correctly, you should receive an HTTP response rather than a Passenger application error.
You can also watch Canvas’s production log:
sudo tail -f /var/canvas/log/production.log
And Apache:
sudo tail -f /var/log/apache2/canvas_error.log
These two logs are extremely useful during initial deployment.
31. Install Let’s Encrypt SSL
Install Certbot:
sudo apt install -y certbot python3-certbot-apache
Request certificates for both Canvas hostnames:
sudo certbot --apache \
-d canvas.example.com \
-d canvasfiles.example.com
Certbot will:
- Validate the DNS records.
- Request the certificates.
- Modify the Apache VirtualHost.
- Configure HTTPS.
- Optionally redirect HTTP to HTTPS.
Verify renewal:
sudo certbot renew --dry-run
Check the timer:
systemctl status certbot.timer
32. Verify HTTPS
Test:
curl -I https://canvas.example.com/
Then:
curl -I https://canvas.example.com/health_check
Open:
https://canvas.example.com
in your browser.
You should reach the Canvas login interface.
Log in using the administrator credentials created during:
RAILS_ENV=production bundle exec rake db:initial_setup
33. Configure Canvas Background Jobs
Canvas relies heavily on background jobs for:
- email notifications
- reports
- statistics
- course operations
- file processing
- scheduled tasks
- imports and exports
- other asynchronous actions
Canvas will not function correctly without the background job worker.
The Canvas source includes:
/var/canvas/script/canvas_init
Create the service link:
sudo ln -s /var/canvas/script/canvas_init /etc/init.d/canvas_init
Register it:
sudo update-rc.d canvas_init defaults
Start it:
sudo /etc/init.d/canvas_init start
Check processes:
ps aux | grep delayed_job
You should see Canvas delayed job workers.
The current Canvas source still contains the canvas_init script, and its purpose is specifically to start and stop Canvas background jobs under RAILS_ENV=production.
For a larger installation, you can dedicate one or more separate Canvas application nodes exclusively to processing these background jobs.
34. Verify Canvas Services
Check PostgreSQL:
sudo systemctl status postgresql
Check Redis:
sudo systemctl status redis-server
Check Apache:
See Also: 🚀 Deploy Poweradmin to Manage PowerDNS on Ubuntu VPS
sudo systemctl status apache2
Check Passenger:
sudo passenger-memory-stats
Check workers:
ps aux | grep delayed_job
Check HTTP:
curl -I https://canvas.example.com/
Check health endpoint:
curl -I https://canvas.example.com/health_check
35. Test Redis from Canvas
You can verify that Rails boots in production mode:
cd /var/canvas
Run:
sudo -u canvasuser \ RAILS_ENV=production \ bundle exec rails runner 'puts Rails.env'
Expected:
production
You can also check Redis directly:
redis-cli ping
Expected:
PONG
36. Test Outgoing Email
After logging into Canvas, configure an account with a valid email address and trigger a notification or password reset.
Watch:
sudo tail -f /var/canvas/log/production.log
If delivery fails, common causes include:
- incorrect SMTP password
- blocked SMTP port
- wrong TLS mode
- invalid sender address
- provider requiring SMTP AUTH
- SPF/DKIM misconfiguration
- VPS provider blocking outbound port 25
Where possible, use authenticated SMTP submission over:
587
rather than sending directly over port 25.
37. Optional: Enable X-Sendfile
If Canvas stores uploaded files locally, Apache’s X-Sendfile support can improve file download performance by allowing Apache to serve file content directly instead of passing it through Rails.
Install:
sudo apt install -y libapache2-mod-xsendfile
Verify:
sudo apachectl -M | grep xsendfile
You should see:
xsendfile_module
Add to the Canvas VirtualHost:
XSendFile On XSendFilePath /var/canvas
Canvas’s production documentation recommends this optimization for installations using local file storage.
Restart Apache:
sudo systemctl restart apache2
38. Understand the Canvas Files Domain
Canvas should ideally use:
canvas.example.com
for the application and:
canvasfiles.example.com
for uploaded content.
This provides a security boundary between the Canvas application and user-controlled files.
Both DNS records can point to the same VPS:
canvas.example.com -> VPS IP canvasfiles.example.com -> VPS IP
and both can be handled by the same Apache configuration.
What matters is that browsers see them as separate origins.
39. Consider Object Storage for Production
Local disk storage works for a small Canvas installation, but it becomes a limitation as course uploads grow.
For a more scalable deployment, consider S3-compatible object storage.
That allows Canvas web servers to remain relatively disposable while uploaded content lives independently.
A more scalable architecture might look like:
+-------------------+
| Load Balancer |
+---------+---------+
|
+---------+---------+
| |
+------v------+ +------v------+
| Canvas Web 1 | | Canvas Web 2 |
+------+------+ +------+-------+
| |
+---------+---------+
|
+--------------------+--------------------+
| | |
+------v------+ +------v------+ +------v------+
| PostgreSQL | | Redis | | Object/S3 |
+-------------+ +-------------+ +-------------+
|
+------v------+
| Canvas Jobs |
+-------------+
For a small VPS, however, running everything on one host is a reasonable starting point.
40. Configure Automatic Security Updates
Install:
See Also: Top 12 Best VPS Control Panels
sudo apt install -y unattended-upgrades
Enable:
sudo dpkg-reconfigure --priority=low unattended-upgrades
Remember that Canvas itself is not updated through Ubuntu’s package manager because it was installed from Git.
41. Back Up Canvas
At minimum, back up:
PostgreSQL
sudo -u postgres pg_dump \
-Fc \
canvas_production \
> /root/canvas_production.dump
Canvas configuration
sudo tar czf /root/canvas-config.tar.gz \
/var/canvas/config
Uploaded files
If using local file storage, back up the Canvas file store as well.
SSL certificates
Certbot certificates live under:
/etc/letsencrypt
A proper backup strategy should therefore protect:
PostgreSQL database Canvas uploaded files /var/canvas/config /etc/letsencrypt custom branding/themes external integration credentials
Do not rely only on a VPS snapshot as your sole backup.
42. Updating Canvas LMS
Before every update:
- Back up PostgreSQL.
- Back up configuration.
- Take a VPS snapshot if available.
- Review Canvas changes before deploying.
- Schedule maintenance if necessary.
Enter Canvas:
cd /var/canvas
Confirm the branch:
git status git branch --show-current
Retrieve updates:
git fetch origin
Update the production branch:
git checkout prod git pull
Install Ruby dependencies:
bundle install
Install JavaScript dependencies:
yarn install
Run database migrations:
RAILS_ENV=production bundle exec rake db:migrate
Recompile assets:
RAILS_ENV=production \ SASS_STYLE=compressed \ bundle exec rake canvas:compile_assets
Canvas’s production documentation also recommends regenerating existing themes after subsequent updates:
RAILS_ENV=production \ bundle exec rake brand_configs:generate_and_upload_all
Restore ownership:
sudo chown -R canvasuser:canvasuser \
/var/canvas/log \
/var/canvas/tmp \
/var/canvas/public/assets \
/var/canvas/public/dist
Restart workers:
sudo /etc/init.d/canvas_init restart
Restart Apache:
sudo systemctl restart apache2
Then test:
curl -I https://canvas.example.com/health_check
43. Useful Troubleshooting Commands
Check Canvas
sudo tail -100 /var/canvas/log/production.log
Or follow live:
sudo tail -f /var/canvas/log/production.log
Check Apache
sudo tail -100 /var/log/apache2/canvas_error.log
Check Apache syntax
sudo apachectl configtest
Expected:
Syntax OK
Check Passenger
sudo passenger-config validate-install
And:
sudo passenger-memory-stats
Check PostgreSQL
sudo systemctl status postgresql
Test the database:
psql -h localhost \
-U canvas \
-d canvas_production
Check Redis
redis-cli ping
Expected:
PONG
Check Canvas workers
ps aux | grep delayed_job
Verify the installed versions
ruby --version node --version yarn --version psql --version redis-server --version apache2 -v
44. Canvas Returns HTTP 500
Start with:
sudo tail -100 /var/canvas/log/production.log
Then:
sudo tail -100 /var/log/apache2/canvas_error.log
Common causes are:
See Also: Laravel vs Symfony: A Comprehensive Comparison of PHP Frameworks
- database credentials are incorrect
security.ymlis incomplete- Canvas cannot read configuration files
- Passenger is using the wrong Ruby
- database initialization was not completed
- pending database migrations
- Redis configuration is invalid
- Canvas runtime directories are not writable
Check database state:
cd /var/canvas RAILS_ENV=production bundle exec rake db:migrate:status
45. Canvas Loads but Has No CSS
This almost always indicates a problem with compiled assets or static asset delivery.
Recompile:
cd /var/canvas
Then:
RAILS_ENV=production \ SASS_STYLE=compressed \ bundle exec rake canvas:compile_assets
Check:
ls -lah public/dist
And:
ls -lah public/assets
Repair ownership:
sudo chown -R canvasuser:canvasuser \
public/dist \
public/assets
Then restart Apache:
sudo systemctl restart apache2
Asset compilation is mandatory; Canvas may otherwise answer requests while presenting a broken interface.
46. Passenger Reports a Ruby Error
Check what Canvas itself sees:
cd /var/canvas ruby --version bundle exec ruby --version
Then inspect Passenger:
sudo passenger-config about ruby-command
If Passenger is using the wrong Ruby, explicitly configure:
PassengerRuby /usr/bin/ruby
then:
sudo systemctl restart apache2
47. Background Tasks Are Not Running
Check:
ps aux | grep delayed_job
Restart:
sudo /etc/init.d/canvas_init restart
Then examine:
sudo tail -f /var/canvas/log/delayed_job.log
and:
sudo tail -f /var/canvas/log/production.log
Pay particular attention to file permissions on:
/var/canvas/config /var/canvas/log /var/canvas/tmp
Canvas workers need to read the protected configuration files and write to their runtime directories.
48. Production Security Checklist
Before opening the LMS to users, verify all of the following:
- HTTPS is enabled.
- HTTP redirects to HTTPS.
- PostgreSQL is not exposed publicly.
- Redis is not exposed publicly.
- Canvas runs as
canvasuser, not root. - Configuration files are protected.
- SSH root authentication is restricted where practical.
- Password authentication is disabled if SSH keys are used.
- UFW or another firewall is configured.
- Automatic operating-system security updates are enabled.
- SMTP credentials are protected.
- PostgreSQL backups are automated.
- User-uploaded files are backed up.
- Off-server backups exist.
- DNS has been configured correctly.
- SPF/DKIM/DMARC are configured for outgoing mail.
- Canvas background workers are running.
/health_checkresponds normally.- PostgreSQL, Redis, Apache, and Canvas logs are monitored.
49. Recommended Production Architecture
For a small organization:
Ubuntu VPS | +-- Apache + Passenger | +-- Canvas LMS | +-- Canvas delayed jobs | +-- PostgreSQL | +-- Redis | +-- Local or S3-compatible file storage
For modest production usage, start with roughly:
See Also: Which Windows Server Versions are Available for Windows VPS?
4-8 vCPU 8-16 GB RAM 100+ GB NVMe
For heavier usage, separate components:
2+ Canvas web servers 1+ Canvas jobs server Dedicated PostgreSQL Dedicated Redis S3-compatible object storage Load balancer Central backups Monitoring
Canvas’s own production documentation explicitly notes that the web application, database, and job processing can be separated and that busy environments benefit from moving job processing onto dedicated nodes.
50. Final Verification
Run:
curl -I https://canvas.example.com/
Then:
curl -I https://canvas.example.com/health_check
Check all major services:
sudo systemctl status apache2 sudo systemctl status postgresql sudo systemctl status redis-server
Check Passenger:
sudo passenger-memory-stats
Check jobs:
ps aux | grep delayed_job
Finally visit:
https://canvas.example.com
and log in with the administrator account created during db:initial_setup.
You now have a self-hosted Canvas LMS installation running on an Ubuntu VPS with PostgreSQL, Redis, Apache, Passenger, HTTPS, and Canvas background processing.
Important Version Note
Canvas LMS is under active development, so dependency requirements can change relatively quickly. As of the current Canvas source and production documentation:
Ubuntu reference platform: 22.04 LTS Ruby: 3.4.1+ Node.js: 20+ PostgreSQL: 14+ Redis: 6+ Yarn: 1.x / ^1.19.1 Web server: Apache + Passenger Production branch: prod
Instructure’s Production Start documentation was most recently updated in May 2026, and the source tree currently pins Ruby 3.4.1 in Gemfile.lock while requiring Node.js 20 or later in package.json.
Before installing a future Canvas release or performing a major upgrade, always compare these values with the current prod branch instead of assuming an older Canvas installation guide remains accurate.
Conclusion
You now know how to install Canvas LMS on Ubuntu VPS.









