{"id":1035,"date":"2026-11-08T13:00:00","date_gmt":"2026-11-08T13:00:00","guid":{"rendered":"https:\/\/vpslake.com\/blog\/?p=1035"},"modified":"2026-11-08T13:00:00","modified_gmt":"2026-11-08T13:00:00","slug":"install-concrete5-debian-11-lamp","status":"publish","type":"post","link":"https:\/\/vpslake.com\/blog\/2026\/11\/08\/install-concrete5-debian-11-lamp\/","title":{"rendered":"How to Install Concrete5 on Debian 11 (LAMP)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Install concrete5 on Debian 11 by building an Apache, MariaDB, and PHP 7.4 stack, then open the browser installer to publish your first site. This guide uses Concrete CMS 8.5.21, the compatible 8.5 branch, and takes about 20 minutes once your domain points to the VPS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>A 64-bit Debian GNU\/Linux 11 (Bullseye) VPS with at least 2 GB of RAM and 10 GB of free disk space.<\/li>\n\n\n<li>SSH access as <code>root<\/code>, or a normal account with working <code>sudo<\/code> privileges.<\/li>\n\n\n<li>A domain such as <code>YOUR_DOMAIN<\/code> with an A record pointing to the VPS. You can test over the server IP first, but HTTPS requires a domain.<\/li>\n\n\n<li>A strong site administrator password and a separate, long MariaDB password.<\/li>\n\n\n<li>Debian 11 is past its normal support window, so use this for an existing Bullseye server and plan an upgrade for a new public site. A <a href=\"https:\/\/vpslake.com\/buy-vps\">VPSLake Linux VPS<\/a> provides the root access needed for this setup.<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Concrete CMS 8.5.21 is used because Debian 11 provides PHP 7.4. The <a href=\"https:\/\/documentation.concretecms.org\/9-x\/developers\/introduction\/system-requirements\" target=\"_blank\" rel=\"noopener\">Concrete CMS system requirements<\/a> also list the PHP extensions, Apache support, and MariaDB compatibility used below.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The commands use <code>sudo<\/code>. If you are logged in as <code>root<\/code>, remove <code>sudo<\/code> from each command.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Confirm Bullseye and install the LAMP packages<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Checking the release first prevents you from installing packages meant for Debian 12 or 13.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>grep -E '^(PRETTY_NAME|VERSION_CODENAME)=' \/etc\/os-release\ndpkg --print-architecture\nsudo apt update\nsudo apt install -y apache2 mariadb-server php7.4 php7.4-cli libapache2-mod-php7.4 php7.4-mysql php7.4-gd php7.4-curl php7.4-xml php7.4-mbstring php7.4-zip php7.4-opcache unzip curl ca-certificates ufw certbot python3-certbot-apache<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>libapache2-mod-php7.4<\/code> lets Apache execute PHP, while the other PHP packages provide Concrete&#8217;s database, image, XML, archive, and multibyte features. The check should show Bullseye and a normal VPS architecture:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PRETTY_NAME=\"Debian GNU\/Linux 11 (bullseye)\"\nVERSION_CODENAME=bullseye\namd64<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Set a reasonable PHP memory and upload limit before the installer runs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo tee \/etc\/php\/7.4\/apache2\/conf.d\/99-concrete.ini &gt;\/dev\/null &lt;&lt;'EOF'\nmemory_limit = 256M\nupload_max_filesize = 64M\npost_max_size = 64M\nmax_execution_time = 120\nEOF\nsudo a2enmod rewrite headers\nsudo systemctl enable --now apache2 mariadb\nsudo systemctl restart apache2<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Create the Concrete database<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Concrete stores pages, users, configuration, and file metadata in MariaDB, so give it its own database and least-scope account.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run the hardening wizard first and remove anonymous accounts, the test database, and remote root access when prompted:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mariadb-secure-installation<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Open MariaDB as the local administrative account:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mariadb<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Paste these statements at the <code>MariaDB [(none)]&gt;<\/code> prompt, replacing <code>YOUR_DB_PASSWORD<\/code> with a unique password. Do not reuse the VPS or Concrete administrator password.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE concrete CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nCREATE USER 'concrete_user'@'localhost' IDENTIFIED BY 'YOUR_DB_PASSWORD';\nGRANT ALL PRIVILEGES ON concrete.* TO 'concrete_user'@'localhost';\nFLUSH PRIVILEGES;\nEXIT;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>EXIT;<\/code> line returns you to the shell. The database name, user, host, and password will be entered in the browser installer later.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Download Concrete CMS 8.5.21<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Using the official release archive avoids an unpinned Git checkout and keeps the PHP 7.4-compatible version explicit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Download the release, extract it into the site directory, and remove the temporary archive:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo install -d -o www-data -g www-data \/var\/www\/concrete\ncurl -fL https:\/\/www.concretecms.org\/download_file\/cdc12556-d0cc-4ca4-85cc-b1e76b6a4401\/2718 -o \/tmp\/concretecms-8.5.21.zip\nsudo unzip -q \/tmp\/concretecms-8.5.21.zip -d \/var\/www\/concrete\nrm -f \/tmp\/concretecms-8.5.21.zip<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The archive should leave <code>index.php<\/code>, <code>concrete\/<\/code>, <code>application\/<\/code>, <code>packages\/<\/code>, and <code>updates\/<\/code> directly under <code>\/var\/www\/concrete<\/code>. Confirm the expected entry point exists:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>test -f \/var\/www\/concrete\/index.php &amp;&amp; echo \"Concrete CMS files extracted\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Concrete CMS files extracted<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/documentation.concretecms.org\/9-x\/developers\/introduction\/installing-concrete-cms\" target=\"_blank\" rel=\"noopener\">official Concrete CMS installation guide<\/a> uses the same download, extraction, database, and web-installer flow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Configure Apache and file permissions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Apache needs a named virtual host and rewrite support so Concrete can turn friendly page URLs into application requests.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Create a virtual host, replacing every <code>YOUR_DOMAIN<\/code> occurrence before running the block:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo tee \/etc\/apache2\/sites-available\/YOUR_DOMAIN.conf &gt;\/dev\/null &lt;&lt;'APACHE'\n&lt;VirtualHost *:80&gt;\n    ServerName YOUR_DOMAIN\n    DocumentRoot \/var\/www\/concrete\n\n    ErrorLog ${APACHE_LOG_DIR}\/concrete_error.log\n    CustomLog ${APACHE_LOG_DIR}\/concrete_access.log combined\n\n    &lt;Directory \/var\/www\/concrete&gt;\n        Options FollowSymLinks\n        AllowOverride All\n        Require all granted\n    &lt;\/Directory&gt;\n&lt;\/VirtualHost&gt;\nAPACHE\nsudo a2dissite 000-default.conf\nsudo a2ensite YOUR_DOMAIN.conf\nsudo apache2ctl configtest\nsudo systemctl reload apache2<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Syntax OK<\/code> confirms Apache accepted the configuration. Now make the web server the owner, keep ordinary files non-executable, and grant write access only to directories Concrete must update:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo chown -R www-data:www-data \/var\/www\/concrete\nsudo find \/var\/www\/concrete -type d -exec chmod 0755 {} +\nsudo find \/var\/www\/concrete -type f -exec chmod 0644 {} +\nsudo chmod 0775 \/var\/www\/concrete\/application\/files \/var\/www\/concrete\/application\/config \/var\/www\/concrete\/packages \/var\/www\/concrete\/updates<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Open the web ports and enable HTTPS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Allow SSH before enabling UFW so your current administration session is not cut off, then open only HTTP and HTTPS for the site.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ufw allow OpenSSH\nsudo ufw allow 80\/tcp\nsudo ufw allow 443\/tcp\nsudo ufw --force enable\nsudo ufw status verbose<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see <code>22\/tcp<\/code>, <code>80\/tcp<\/code>, and <code>443\/tcp<\/code> allowed. If SSH uses a custom port, allow that port instead of <code>OpenSSH<\/code> before enabling the firewall.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After the domain resolves to this server, request a certificate and let Certbot add the HTTPS virtual host and redirect:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo certbot --apache -d YOUR_DOMAIN --redirect --agree-tos --no-eff-email -m YOUR_EMAIL<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Replace <code>YOUR_EMAIL<\/code> with an address that can receive certificate expiry notices. Certbot must be able to reach port 80 from the public internet; the command will fail if DNS or the firewall still points elsewhere.<\/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 browser wizard writes the site configuration after it can reach both Apache and MariaDB.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Open this address:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:\/\/YOUR_DOMAIN<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On the requirements screen, continue only when the checks are green. For database settings, use:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Database server: <code>localhost<\/code><\/li>\n\n\n<li>Database name: <code>concrete<\/code><\/li>\n\n\n<li>Database user: <code>concrete_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\">Choose a site name, enter an administrator email, and create a unique administrator password. Complete the installation, then sign in to the dashboard and publish a small test page.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Verify the Concrete CMS installation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">These checks confirm that the services are running, Apache loaded PHP, the required writable directories are usable, and the public site returns a response:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>php --version\nsudo systemctl is-active apache2 mariadb\napache2ctl -M | grep php\nsudo -u www-data test -w \/var\/www\/concrete\/application\/files &amp;&amp; echo \"files directory writable\"\ncurl -fsSI https:\/\/YOUR_DOMAIN | sed -n '1,5p'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected output includes PHP 7.4, two <code>active<\/code> lines, a PHP module, the writable-directory message, and an HTTP success or redirect response:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PHP 7.4.x (cli) ...\nactive\nactive\n php7_module (shared)\nfiles directory writable\nHTTP\/2 200<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sign in to Concrete CMS, create a page, and open it in a private browser window. A page that loads over HTTPS proves the web server, PHP, database connection, rewrites, and certificate are working together.<\/p>\n\n\n\n<aside class=\"vl-element vl-cta-banner\" aria-label=\"Launch a Concrete CMS Site\">\n\t<div class=\"vl-cta-banner__content\">\n\t\t<p class=\"vl-cta-banner__eyebrow\">Debian VPS<\/p>\n\t\t<h3 class=\"vl-cta-banner__title\">Launch a Concrete CMS Site<\/h3>\n\t\t<p class=\"vl-cta-banner__text\">Install Concrete CMS on a Debian VPS with root access and the resources for your next website.<\/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\"><code>apt update<\/code> returns a 404 for <code>bullseye-security<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Debian 11 reached the end of its LTS period on August 31, 2026, so old images may still reference mirrors that no longer carry Bullseye security metadata. For a temporary legacy installation, point APT at the Debian archive, then schedule an OS upgrade instead of treating the archive as current security support.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo cp -a \/etc\/apt\/sources.list \/etc\/apt\/sources.list.before-bullseye-archive\nsudo tee \/etc\/apt\/sources.list &gt;\/dev\/null &lt;&lt;'EOF'\ndeb http:\/\/archive.debian.org\/debian bullseye main contrib non-free\ndeb http:\/\/archive.debian.org\/debian bullseye-updates main contrib non-free\nEOF\nsudo find \/etc\/apt\/sources.list.d -type f -name '*.list' -exec sed -i '\/bullseye-security\/d' {} +\nsudo apt -o Acquire::Check-Valid-Until=false update<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If this is a new deployment, rebuild it on a supported Debian release instead. Do not expose an unpatched Bullseye server as a permanent production host.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Apache shows <code>403 Forbidden<\/code> or Concrete says folders are not writable<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The web-server account cannot read the site or write one of Concrete&#8217;s application directories. Reapply the owner and the four narrowly scoped write permissions, then reload Apache:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo chown -R www-data:www-data \/var\/www\/concrete\nsudo chmod 0775 \/var\/www\/concrete\/application\/files \/var\/www\/concrete\/application\/config \/var\/www\/concrete\/packages \/var\/www\/concrete\/updates\nsudo systemctl reload apache2<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid <code>chmod 777<\/code>; it gives every local account write access to the CMS.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>SQLSTATE<\/code> or \u201cUnable to connect to the database\u201d<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The installer values do not match the MariaDB account, or the account was created for a different host. Check the account without printing its password, then test authentication:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mariadb -e \"SHOW DATABASES LIKE 'concrete'; SELECT User,Host FROM mysql.user WHERE User='concrete_user';\"\nmariadb -u concrete_user -p -h localhost concrete -e 'SELECT 1;'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Enter the same database name, user, password, and <code>localhost<\/code> host in the wizard. The second command should print <code>1<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The site loads the Apache default page or returns <code>404<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The domain may not match the virtual host, or Apache may not have reloaded the file. Confirm the DNS target, site status, rewrite module, and configuration syntax:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo a2query -s YOUR_DOMAIN.conf\nsudo a2query -m rewrite\nsudo apache2ctl configtest\nsudo systemctl reload apache2<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Browse to <code>https:\/\/YOUR_DOMAIN<\/code>, not the raw IP, after DNS and the certificate are ready.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Can I install the latest Concrete CMS 9 on Debian 11?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Not with Debian 11&#8217;s default PHP 7.4 as a general recommendation. This guide pins Concrete CMS 8.5.21 because it is the compatible 8.5 release; use a supported Debian release and a current PHP version for a new Concrete CMS 9 deployment.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Does Concrete CMS need Composer for this installation?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. The official release archive contains the application and its dependencies, so the web installer can be used directly. Composer is useful for development workflows, but it adds no benefit to this pinned archive-based server install.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can I use an IP address instead of a domain?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You can use the VPS IP for an initial HTTP check, but a normal trusted HTTPS certificate is issued for a domain name. Set the DNS A record first, then run Certbot with that domain.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Which directories must Concrete CMS write to?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The installer and later updates need <code>application\/files\/<\/code>, <code>application\/config\/<\/code>, <code>packages\/<\/code>, and <code>updates\/<\/code>. The guide makes only those directories group-writable and keeps regular files at mode <code>0644<\/code>.<\/p>\n\n\n\n<script type=\"application\/ld+json\">{\"@context\": \"https:\/\/schema.org\", \"@type\": \"FAQPage\", \"mainEntity\": [{\"@type\": \"Question\", \"name\": \"Can I install the latest Concrete CMS 9 on Debian 11?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"Not with Debian 11's default PHP 7.4 as a general recommendation. This guide pins Concrete CMS 8.5.21 because it is the compatible 8.5 release; use a supported Debian release and a current PHP version for a new Concrete CMS 9 deployment.\"}}, {\"@type\": \"Question\", \"name\": \"Does Concrete CMS need Composer for this installation?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"No. The official release archive contains the application and its dependencies, so the web installer can be used directly. Composer is useful for development workflows, but it adds no benefit to this pinned archive-based server install.\"}}, {\"@type\": \"Question\", \"name\": \"Can I use an IP address instead of a domain?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"You can use the VPS IP for an initial HTTP check, but a normal trusted HTTPS certificate is issued for a domain name. Set the DNS A record first, then run Certbot with that domain.\"}}, {\"@type\": \"Question\", \"name\": \"Which directories must Concrete CMS write to?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"The installer and later updates need `application\/files\/`, `application\/config\/`, `packages\/`, and `updates\/`. The guide makes only those directories group-writable and keeps regular files at mode `0644`.\"}}]}<\/script>\n\n","protected":false},"excerpt":{"rendered":"Install concrete5 on Debian 11 with Apache, MariaDB, PHP 7.4, HTTPS, and secure permissions, then finish the web installer and fix common errors.","protected":false},"author":1,"featured_media":1036,"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":[135,134,96,117],"class_list":["post-1035","post","type-post","status-publish","format-standard","has-post-thumbnail","category-guides","category-vps-hosting","tag-concrete-cms","tag-concrete5","tag-debian-11","tag-lamp-stack","cs-entry"],"_links":{"self":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/1035","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=1035"}],"version-history":[{"count":1,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/1035\/revisions"}],"predecessor-version":[{"id":3941,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/1035\/revisions\/3941"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/media\/1036"}],"wp:attachment":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/media?parent=1035"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/categories?post=1035"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/tags?post=1035"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}