{"id":1029,"date":"2026-11-07T18:00:00","date_gmt":"2026-11-07T18:00:00","guid":{"rendered":"https:\/\/vpslake.com\/blog\/?p=1029"},"modified":"2026-11-07T18:00:00","modified_gmt":"2026-11-07T18:00:00","slug":"install-chamilo-lemp-almalinux-8","status":"publish","type":"post","link":"https:\/\/vpslake.com\/blog\/2026\/11\/07\/install-chamilo-lemp-almalinux-8\/","title":{"rendered":"How to Install Chamilo with LEMP on AlmaLinux 8"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Install Chamilo with LEMP Stack on AlmaLinux 8 by putting Chamilo LMS 1.11.40 behind Nginx, MariaDB, PHP-FPM, SELinux, and HTTPS. With DNS already pointed to the VPS, you can reach the portal and create its first administrator in about 15\u201320 minutes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>AlmaLinux 8.10 or another current AlmaLinux 8 update, with 2 vCPUs, at least 2 GB RAM, and 10 GB of free disk space.<\/li>\n\n\n<li>A root account or a sudo-enabled user connected over SSH. The commands below use <code>sudo<\/code>.<\/li>\n\n\n<li>A domain such as <code>YOUR_DOMAIN<\/code> with an A record pointing to the server. Ports 80 and 443 must be reachable from the internet for Let\u2019s Encrypt.<\/li>\n\n\n<li>A unique database password, a separate Chamilo administrator password, and an email address for certificate notices.<\/li>\n\n\n<li>A fresh <a href=\"https:\/\/vpslake.com\/buy-vps\">VPSLake Linux VPS<\/a> with a public IPv4 address is suitable for this installation.<\/li>\n\n\n<li>Do not run Apache on ports 80 or 443; this guide installs Nginx as the web server.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Install PHP, Nginx, MariaDB, and firewalld<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">AlmaLinux 8 uses DNF, while Chamilo 1.11.40\u2019s official download is compatible with PHP 8.3, so select the Remi PHP 8.3 stream before installing the stack.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf update -y\nsudo dnf install -y epel-release firewalld mariadb-server nginx unzip curl bind-utils policycoreutils-python-utils\nsudo dnf install -y https:\/\/rpms.remirepo.net\/enterprise\/remi-release-8.rpm\nsudo dnf module reset php -y\nsudo dnf module enable php:remi-8.3 -y\nsudo dnf install -y php php-cli php-fpm php-curl php-gd php-intl php-mbstring php-mysqlnd php-opcache php-xml php-zip php-ldap php-soap\nsudo sed -ri 's\/^[; ]*user = .*\/user = nginx\/; s\/^[; ]*group = .*\/group = nginx\/; s\/^[; ]*listen.owner = .*\/listen.owner = nginx\/; s\/^[; ]*listen.group = .*\/listen.group = nginx\/' \/etc\/php-fpm.d\/www.conf\nsudo sed -ri 's\/^memory_limit = .*\/memory_limit = 256M\/; s\/^upload_max_filesize = .*\/upload_max_filesize = 100M\/; s\/^post_max_size = .*\/post_max_size = 100M\/; s\/^max_execution_time = .*\/max_execution_time = 300\/; s\/^max_input_time = .*\/max_input_time = 600\/; s\/^max_input_vars = .*\/max_input_vars = 3000\/' \/etc\/php.ini\nsudo systemctl enable --now firewalld mariadb nginx php-fpm\nsudo systemctl restart php-fpm\nphp -v\nnginx -v\nmariadb --version<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The output should show PHP 8.3.x, an Nginx version, and a MariaDB client version. The exact patch numbers will change as the repositories publish updates.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Create a dedicated MariaDB database<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Chamilo stores users, courses, and settings in MariaDB, so give this portal a local-only account with privileges limited to its own database.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mysql_secure_installation<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When the hardening wizard asks, remove anonymous users, disable remote root login, remove the test database, and reload the privilege tables. Then create the database and account; replace <code>YOUR_DB_PASSWORD<\/code> before running the block.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mariadb &lt;&lt;'SQL'\nCREATE DATABASE chamilo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nCREATE USER 'chamilo_user'@'localhost' IDENTIFIED BY 'YOUR_DB_PASSWORD';\nGRANT ALL PRIVILEGES ON chamilo.* TO 'chamilo_user'@'localhost';\nFLUSH PRIVILEGES;\nSQL\nsudo mariadb -e \"SHOW DATABASES LIKE 'chamilo';\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The final command should print a row named <code>chamilo<\/code>. Keep the database name, username, and password for the browser installer; MariaDB does not need a public firewall rule because the application connects through localhost.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Download Chamilo 1.11.40 and set safe permissions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use the project\u2019s numbered release archive instead of an old development snapshot, then make only Chamilo\u2019s documented upload and configuration directories writable by Nginx.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/chamilo.org\/en\/download\/\" target=\"_blank\" rel=\"noopener\">official Chamilo download page<\/a> lists 1.11.40 for PHP 7.4 and 8.3. Download that release and copy its files into <code>\/var\/www\/chamilo<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CHAMILO_VERSION=1.11.40\nsudo curl -fL -o \"\/tmp\/chamilo-${CHAMILO_VERSION}.zip\" \"https:\/\/github.com\/chamilo\/chamilo-lms\/releases\/download\/v${CHAMILO_VERSION}\/chamilo-${CHAMILO_VERSION}.zip\"\nsudo mkdir -p \"\/tmp\/chamilo-${CHAMILO_VERSION}-extract\" \/var\/www\/chamilo\nsudo unzip -q \"\/tmp\/chamilo-${CHAMILO_VERSION}.zip\" -d \"\/tmp\/chamilo-${CHAMILO_VERSION}-extract\"\nCHAMILO_ROOT=\"$(find \"\/tmp\/chamilo-${CHAMILO_VERSION}-extract\" -type f -path '*\/main\/install\/index.php' -printf '%h\\n' | sed 's#\/main\/install$##' | head -n 1)\"\ntest -n \"$CHAMILO_ROOT\"\nsudo cp -a \"$CHAMILO_ROOT\"\/. \/var\/www\/chamilo\/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Keep the application readable by Nginx, but grant it write access only where the installer and course uploads require it. This avoids the world-writable permissions used by many older Chamilo tutorials.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo chown -R root:nginx \/var\/www\/chamilo\nsudo find \/var\/www\/chamilo -type d -exec chmod 0750 {} \\;\nsudo find \/var\/www\/chamilo -type f -exec chmod 0640 {} \\;\nsudo chown -R nginx:nginx \/var\/www\/chamilo\/app \/var\/www\/chamilo\/web \/var\/www\/chamilo\/main\/default_course_document\/images \/var\/www\/chamilo\/main\/lang\nsudo find \/var\/www\/chamilo\/app \/var\/www\/chamilo\/web \/var\/www\/chamilo\/main\/default_course_document\/images \/var\/www\/chamilo\/main\/lang -type d -exec chmod 0770 {} \\;\nsudo find \/var\/www\/chamilo\/app \/var\/www\/chamilo\/web \/var\/www\/chamilo\/main\/default_course_document\/images \/var\/www\/chamilo\/main\/lang -type f -exec chmod 0660 {} \\;\nsudo semanage fcontext -a -t httpd_sys_content_t '\/var\/www\/chamilo(\/.*)?'\nsudo semanage fcontext -a -t httpd_sys_rw_content_t '\/var\/www\/chamilo\/(app|web|main\/default_course_document\/images|main\/lang)(\/.*)?'\nsudo restorecon -Rv \/var\/www\/chamilo<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The persistent SELinux contexts allow Nginx and PHP-FPM to read the code and write Chamilo data without disabling SELinux.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Configure Nginx for Chamilo routes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Chamilo 1.11 uses special routes for course files, SCORM content, badges, and clean URLs; a generic PHP front-controller rule is not enough. Create the server block below and replace <code>YOUR_DOMAIN<\/code> before running it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo tee \/etc\/nginx\/conf.d\/chamilo.conf &gt; \/dev\/null &lt;&lt;'NGINX'\nserver {\n    listen 80;\n    listen [::]:80;\n    server_name YOUR_DOMAIN;\n\n    root \/var\/www\/chamilo;\n    index index.php;\n    charset utf-8;\n    client_max_body_size 100m;\n    autoindex off;\n\n    location @rewrite {\n        rewrite ^\/certificates\/$ \/certificates\/index.php last;\n        rewrite ^\/courses\/([^\/]+)\/$ \/main\/course_home\/course_home.php?cDir=$1 last;\n        rewrite ^\/courses\/([^\/]+)\/index.php$ \/main\/course_home\/course_home.php?cDir=$1 last;\n        rewrite ^\/courses\/([^\/]+)\/scorm\/(.*\\.(js|css|png|jpg|jpeg|gif))$ \/app\/courses\/$1\/scorm\/$2 last;\n        rewrite ^\/courses\/([^\/]+)\/scorm\/(.*)$ \/main\/document\/download_scorm.php?doc_url=\/$2&amp;cDir=$1 last;\n        rewrite ^\/courses\/([^\/]+)\/document\/certificates\/(.*)$ \/app\/courses\/$1\/document\/certificates\/$2 last;\n        rewrite ^\/courses\/([^\/]+)\/document\/(.*)$ \/main\/document\/download.php?doc_url=\/$2&amp;cDir=$1 last;\n        rewrite ^\/courses\/([^\/]+)\/upload\/([^\/]+)\/(.*)$ \/main\/document\/download_uploaded_files.php?code=$1&amp;type=$2&amp;file=$3 last;\n        rewrite ^\/courses\/([^\/]+)\/work\/(.*)$ \/main\/work\/download.php?file=work\/$2&amp;cDir=$1 last;\n        rewrite ^\/courses\/([^\/]+)\/upload\/course_home_icons\/(.*\\.(png|jpg|jpeg|gif))$ \/app\/courses\/$1\/upload\/course_home_icons\/$2 last;\n        rewrite ^\/courses\/([^\/]+)\/(.*)$ \/app\/courses\/$1\/$2 last;\n        rewrite ^\/session\/(\\d+)\/about\/?$ \/main\/session\/about.php?session_id=$1 last;\n        rewrite ^\/course\/(\\d+)\/about\/?$ \/main\/course_info\/about.php?course_id=$1 last;\n        rewrite ^\/badge\/(\\d+) \/main\/badge\/issued.php?issue=$1 last;\n        rewrite ^\/skill\/(\\d+)\/user\/(\\d+) \/main\/badge\/issued_all.php?skill=$1&amp;user=$2 last;\n        rewrite ^\/badge\/(\\d+)\/user\/(\\d+) \/main\/badge\/issued_all.php?skill=$1&amp;user=$2 last;\n        rewrite ^\/main\/exercice\/(.*)$ \/main\/exercise\/$1 last;\n        rewrite ^\/main\/newscorm\/(.*)$ \/main\/lp\/$1 last;\n        rewrite ^\/service\/(\\d+)$ \/plugin\/buycourses\/src\/service_information.php?service_id=$1 last;\n        rewrite ^\/main\/upload\/users\/(.*)\/(.*)\/my_files\/(.*)$ \/app\/upload\/users\/$1\/$2\/my_files\/$3 last;\n        try_files $uri \/index.php$is_args$args;\n    }\n\n    location \/ {\n        try_files $uri @rewrite;\n    }\n\n    location \/main {\n        rewrite ^\/main\/([^\/]+)\/?$ \/main\/$1\/index.php last;\n    }\n\n    location ~ \\.php$ {\n        try_files $uri @rewrite;\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        fastcgi_param HTTPS $https;\n        fastcgi_pass unix:\/run\/php-fpm\/www.sock;\n    }\n\n    location ~* \\.(png|jpe?g|gif|ico|js|css|mp3|swf|flv|mp4|ogg|woff|woff2)$ {\n        rewrite ^\/courses\/([^\/]+)\/course-pic85x85.png$ \/app\/courses\/$1\/course-pic85x85.png last;\n        rewrite ^\/courses\/([^\/]+)\/course-pic.png$ \/app\/courses\/$1\/course-pic.png last;\n        rewrite ^\/courses\/([^\/]+)\/scorm\/(.*)$ \/main\/document\/download_scorm.php?doc_url=\/$2&amp;cDir=$1 last;\n        rewrite ^\/courses\/([^\/]+)\/document\/(.*)$ \/main\/document\/download.php?doc_url=\/$2&amp;cDir=$1 last;\n        rewrite ^\/courses\/([^\/]+)\/work\/(.*)$ \/main\/work\/download.php?file=work\/$2&amp;cDir=$1 last;\n        rewrite ^\/courses\/([^\/]+)\/upload\/(.*)$ \/app\/courses\/$1\/upload\/$2 last;\n        rewrite ^\/courses\/(.*)$ \/app\/courses\/$1 break;\n        expires 30d;\n        access_log off;\n        try_files $uri @rewrite;\n    }\n\n    location ^~ \/app\/config\/ {\n        deny all;\n    }\n\n    location ^~ \/vendor\/ {\n        deny all;\n    }\n\n    location ~ \/\\.(?!well-known) {\n        deny all;\n    }\n\n    location ^~ \/tests\/ {\n        deny all;\n    }\n\n    location ^~ \/main\/inc\/lib\/javascript\/bigupload\/files\/ {\n        deny all;\n    }\n\n    location ~* \/web\\.config$ {\n        deny all;\n        return 403;\n    }\n}\nNGINX\nsudo nginx -t\nsudo systemctl reload nginx<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The syntax check should return <code>syntax is ok<\/code> and <code>test is successful<\/code>. The <code>root<\/code> must remain <code>\/var\/www\/chamilo<\/code>; changing it to a subdirectory breaks Chamilo\u2019s course and document routes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Open the firewall and enable HTTPS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Allow SSH for administration, HTTP for the ACME challenge, and HTTPS for the finished portal before requesting the certificate.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo firewall-cmd --permanent --zone=public --add-service=ssh\nsudo firewall-cmd --permanent --zone=public --add-service=http\nsudo firewall-cmd --permanent --zone=public --add-service=https\nsudo firewall-cmd --reload\nsudo firewall-cmd --list-services\nsudo dnf install -y certbot python3-certbot-nginx\nsudo certbot --nginx --redirect --agree-tos --no-eff-email -m YOUR_EMAIL -d YOUR_DOMAIN<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The service list should include <code>ssh<\/code>, <code>http<\/code>, and <code>https<\/code>. Replace <code>YOUR_EMAIL<\/code> with a monitored address; Certbot must resolve <code>YOUR_DOMAIN<\/code> to this VPS before validation can succeed. Chamilo\u2019s <a href=\"https:\/\/11.chamilo.org\/documentation\/installation_guide.html\" target=\"_blank\" rel=\"noopener\">official 1.11 installation guide<\/a> also documents the PHP requirements, writable directories, and Nginx rewrite behavior used here.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6: Finish the browser installation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The web wizard creates Chamilo\u2019s configuration and administrator account after the server-side dependencies are ready.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Open <code>https:\/\/YOUR_DOMAIN\/main\/install\/index.php<\/code> in your browser. When the database page appears, enter:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Database type: MySQL or MariaDB<\/li>\n\n\n<li>Database host: <code>localhost<\/code><\/li>\n\n\n<li>Database port: <code>3306<\/code><\/li>\n\n\n<li>Database name: <code>chamilo<\/code><\/li>\n\n\n<li>Database user: <code>chamilo_user<\/code><\/li>\n\n\n<li>Database password: the value used for <code>YOUR_DB_PASSWORD<\/code><\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Continue through the wizard, choose the portal language, and create a long, unique administrator password. If the installer reports that a directory is not writable, stop and correct the ownership and SELinux labels from Step 3 rather than making the whole document root writable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After the dashboard loads, protect the generated configuration directory and remove the temporary archive:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo chown -R root:nginx \/var\/www\/chamilo\/app\/config\nsudo find \/var\/www\/chamilo\/app\/config -type d -exec chmod 0750 {} \\;\nsudo find \/var\/www\/chamilo\/app\/config -type f -exec chmod 0640 {} \\;\nsudo rm -f \/tmp\/chamilo-1.11.40.zip<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Verify the Chamilo installation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Run these checks from SSH, then sign in to the HTTPS dashboard and create a small test course. The command checks prove that the services, PHP modules, Nginx configuration, and public TLS endpoint are working together.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl is-active nginx php-fpm mariadb firewalld\nsudo nginx -t\nphp -v | head -n 1\nphp -m | grep -E 'curl|fileinfo|gd|intl|mbstring|mysqli|PDO|xml|zip'\ncurl -fsSI https:\/\/YOUR_DOMAIN\/<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>active\nactive\nactive\nactive\nsyntax is ok\ntest is successful\nPHP 8.3.x (cli) ...\nHTTP\/2 200<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The module list can appear in a different order, and the HTTP status may be a redirect after installation. A working login, course page, and file upload are the final proof that Chamilo\u2019s Nginx rewrites and writable directories are correct.<\/p>\n\n\n\n<aside class=\"vl-element vl-cta-banner\" aria-label=\"Host Chamilo LMS\">\n\t<div class=\"vl-cta-banner__content\">\n\t\t<p class=\"vl-cta-banner__eyebrow\">AlmaLinux VPS<\/p>\n\t\t<h3 class=\"vl-cta-banner__title\">Host Chamilo LMS<\/h3>\n\t\t<p class=\"vl-cta-banner__text\">Deploy a secure Chamilo learning portal with Nginx, MariaDB, PHP, and HTTPS.<\/p>\n\t<\/div>\n\t<a class=\"vl-cta-banner__button\" href=\"\/buy-vps\" aria-label=\"View Linux VPS plans\">View Linux VPS plans<\/a>\n<\/aside>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">502 Bad Gateway<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Nginx cannot reach PHP-FPM, commonly because the service is stopped or the pool socket is owned by another account. Confirm that <code>\/run\/php-fpm\/www.sock<\/code> exists, then restart the pool and Nginx.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl status php-fpm --no-pager\nsudo ls -l \/run\/php-fpm\/www.sock\nsudo systemctl restart php-fpm nginx<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">The installer reports a missing PHP extension<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The PHP stream may have been changed after the packages were installed, or PHP-FPM may still have old workers. Reinstall the modules and restart PHP-FPM before refreshing the wizard.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf install -y php-curl php-gd php-intl php-mbstring php-mysqlnd php-xml php-zip php-ldap php-soap\nsudo systemctl restart php-fpm\nphp -m | grep -E 'curl|gd|intl|mbstring|mysqli|PDO|xml|zip'<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Course links or document downloads return 404<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Chamilo\u2019s clean URLs depend on the long Nginx rewrite block, not only on <code>try_files<\/code>. Check that the active server block has <code>root \/var\/www\/chamilo<\/code>, that <code>YOUR_DOMAIN<\/code> matches the request hostname, and reload after correcting it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo nginx -T | grep -A4 -B2 'server_name YOUR_DOMAIN'\nsudo nginx -t\nsudo systemctl reload nginx<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">The installer shows <code>Permission denied<\/code> or uploads fail<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The PHP process needs write access to <code>app<\/code>, <code>web<\/code>, <code>main\/default_course_document\/images<\/code>, and <code>main\/lang<\/code>, and SELinux needs the matching writable context. Reapply both the ownership and labels from Step 3, then retry the upload.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo chown -R nginx:nginx \/var\/www\/chamilo\/app \/var\/www\/chamilo\/web \/var\/www\/chamilo\/main\/default_course_document\/images \/var\/www\/chamilo\/main\/lang\nsudo restorecon -Rv \/var\/www\/chamilo<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Certbot cannot validate <code>YOUR_DOMAIN<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The DNS record may still point to another address, or an upstream firewall may block port 80. Check the public DNS result, confirm that HTTP is allowed in firewalld and the VPS control panel, then run Certbot again.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dig +short YOUR_DOMAIN\nsudo firewall-cmd --list-services\nsudo certbot --nginx --redirect --agree-tos --no-eff-email -m YOUR_EMAIL -d YOUR_DOMAIN<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Hardening and maintenance<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Keep AlmaLinux, PHP, Nginx, MariaDB, and Chamilo updated; back up the <code>chamilo<\/code> database and <code>\/var\/www\/chamilo\/app<\/code>, <code>\/var\/www\/chamilo\/web<\/code>, and uploaded course data before upgrades.<\/li>\n\n\n<li>Leave SELinux enforcing and keep <code>\/var\/www\/chamilo\/app\/config<\/code> owned by <code>root:nginx<\/code> with no write permission for the web process after installation.<\/li>\n\n\n<li>Test certificate renewal with <code>sudo certbot renew --dry-run<\/code> and keep SSH limited to key-based administrator access where possible.<\/li>\n\n\n<li>Chamilo\u2019s official guide recommends a scheduled request to <code>https:\/\/YOUR_DOMAIN\/main\/cron\/run.php<\/code> for queued notifications; add it only after confirming the endpoint works for your installation.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Does Chamilo 1.11.40 support PHP 8.3?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. The official download page identifies Chamilo 1.11.40 as a release for PHP 7.4 and PHP 8.3. This guide selects PHP 8.3 from Remi so AlmaLinux 8 does not fall back to an older default stream.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can I install Chamilo without a domain?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You can test the portal through an IP address, but a domain is the practical choice for a public LMS. Let\u2019s Encrypt\u2019s normal HTTP validation in this guide requires a DNS name that resolves to the VPS.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why does Chamilo need several writable directories?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The installer writes configuration and Chamilo creates course uploads, generated assets, and language data in those locations. The rest of the application remains owned by root and readable by Nginx, which reduces the damage a compromised web process could cause.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why is the Nginx configuration longer than a typical PHP site?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Chamilo 1.11 has dedicated URLs for courses, SCORM files, document downloads, badges, and legacy PHP endpoints. The rewrite rules translate those URLs to the correct scripts while keeping ordinary static files fast to serve.<\/p>\n\n\n\n<script type=\"application\/ld+json\">{\"@context\": \"https:\/\/schema.org\", \"@type\": \"FAQPage\", \"mainEntity\": [{\"@type\": \"Question\", \"name\": \"Does Chamilo 1.11.40 support PHP 8.3?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"Yes. The official download page identifies Chamilo 1.11.40 as a release for PHP 7.4 and PHP 8.3. This guide selects PHP 8.3 from Remi so AlmaLinux 8 does not fall back to an older default stream.\"}}, {\"@type\": \"Question\", \"name\": \"Can I install Chamilo without a domain?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"You can test the portal through an IP address, but a domain is the practical choice for a public LMS. Let\u2019s Encrypt\u2019s normal HTTP validation in this guide requires a DNS name that resolves to the VPS.\"}}, {\"@type\": \"Question\", \"name\": \"Why does Chamilo need several writable directories?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"The installer writes configuration and Chamilo creates course uploads, generated assets, and language data in those locations. The rest of the application remains owned by root and readable by Nginx, which reduces the damage a compromised web process could cause.\"}}, {\"@type\": \"Question\", \"name\": \"Why is the Nginx configuration longer than a typical PHP site?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"Chamilo 1.11 has dedicated URLs for courses, SCORM files, document downloads, badges, and legacy PHP endpoints. The rewrite rules translate those URLs to the correct scripts while keeping ordinary static files fast to serve.\"}}]}<\/script>\n\n","protected":false},"excerpt":{"rendered":"Install Chamilo with LEMP Stack on AlmaLinux 8 using Nginx, MariaDB, PHP 8.3, SELinux-safe permissions, HTTPS, and Chamilo 1.11.40 for a secure portal.","protected":false},"author":1,"featured_media":1030,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"csco_singular_sidebar":"","csco_page_header_type":"","csco_page_load_nextpost":"","footnotes":""},"categories":[5,9],"tags":[94,130,131,124,125],"class_list":["post-1029","post","type-post","status-publish","format-standard","has-post-thumbnail","category-guides","category-vps-hosting","tag-almalinux-8","tag-chamilo","tag-elearning","tag-lemp","tag-nginx","cs-entry"],"_links":{"self":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/1029","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/comments?post=1029"}],"version-history":[{"count":1,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/1029\/revisions"}],"predecessor-version":[{"id":3931,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/1029\/revisions\/3931"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/media\/1030"}],"wp:attachment":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/media?parent=1029"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/categories?post=1029"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/tags?post=1029"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}