---
tags: magento
GA: UA-167433668-1
title: Magento 2.4 Installation & Extension Tutorial
description: How to install and setup Magento 2.4 on Ubuntu 18.04, and create your own extension.
image: https://static.magento.com/sites/default/files8/2020-08/21998_dx_Commercial%20Social%20Share%20Image%20-%20Magento.jpg
---
Introduction: [Magento 2.4 Introduction](https://hackmd.io/@rgbkoi/BySSWQ6ku)
**Content**
[TOC]
---
Environment:
- DigitalOcean LAMP droplet
- 1 GB/1 (shared) CPU
- 25 GB SSD
- 1000 GB transfer
- Ubuntu 20.04
- access via ssh + rsa key only (no password)
- static ip: [188.166.223.100](http://188.166.223.100)
:::spoiler **personal setup**
- add the following line to `/etc/bash.bashrc`:
```
alias lh='ls -lh'
```
- uncomment the following line in `~/.bashrc`:
```
force_color_prompt=yes
```
- add/create `/etc/vim/vimrc.local`:
```
set relativenumber
set title
```
:::
Prerequisites
---
1. LAMP stack (comes with the droplet)
- install php extensions:
```
# apt install -y php7.4-pdo php7.4-mysqlnd php7.4-opcache php7.4-xml php7.4-gd php7.4-devel php7.4-mysql php7.4-intl php7.4-mbstring php7.4-bcmath php7.4-json php7.4-iconv php7.4-soap
# service apache2 restart
```
- enable the extensions?
- find the php settings file using `php --ini | grep "Loaded Configuration File"`, edit the file according to [this](https://devdocs.magento.com/guides/v2.4/install-gde/prereq/php-settings.html#php-required-set)
- setup magento database according to [this](https://devdocs.magento.com/guides/v2.4/install-gde/prereq/mysql.html#instgde-prereq-mysql-config)
2. Elasticsearch
- choices:
- install on same host (resource-heavy, impacts performance)
- install on different host (hassle)
- [elastic cloud service](https://cloud.elastic.co/home) (needs money, has 14-day trial) :heavy_check_mark:
- setup:
- elastic stack (elasticsearch & kibana)
- I/O optimized
- provider: azure
- region: tokyo
- version: 7.10.2/**7.9.3**/6.8.13 (Magento 2.4 is only tested with ElasticSearch 7.6.x)
- Username: elastic
Password: mDlI7KIvNVV*************
- endpoint: [https://7829e64f95bc4f32b4875e61d98fcc69.japaneast.azure.elastic-cloud.com:9243](https://7829e64f95bc4f32b4875e61d98fcc69.japaneast.azure.elastic-cloud.com:9243)

- verify elastic search installation:
<pre><font color="#8AE234"><b>root@magento</b></font>:<font color="#729FCF"><b>~</b></font># curl -XGET --user elastic:mDlI7KIvNVV************* 'https://7829e64f95bc4f32b4875e61d98fcc69.japaneast.azure.elastic-cloud.com:9243/_cat/health?v&pretty'
epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1611508837 17:20:37 7829e64f95bc4f32b4875e61d98fcc69 green 3 2 26 13 0 0 0 0 - 100.0%</pre>
- add the following lines to `/etc/environment`
```
elastichost='https://7829e64f95bc4f32b4875e61d98fcc69.japaneast.azure.elastic-cloud.com'
elasticport='9243'
elasticuser='elastic'
elasticpw='mDlI7KIvNVV*************'
```
- so that we can use `$elasticpw` in place of the password.
<pre><font color="#8AE234"><b>root@magento</b></font>:<font color="#729FCF"><b>~</b></font># curl -XGET --user $elasticuser:$elasticpw $elastichost:$elasticport'/_cat/health?v&pretty'
epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1611510162 17:42:42 7829e64f95bc4f32b4875e61d98fcc69 green 3 2 28 14 0 0 0 0 - 100.0% </pre>
3. Setup Magento file system owner
- Add user (malleo) to the web server group `www-data`, use `--disabled-password` so that this user can only log in via ssh rsa.
- Copy `root`'s ssh config (incl. authorized keys) to `/home/<user>/`, and give the user access to their `.ssh` folder.
- extend write access of `/var/www/html` to the `www-data` group
<pre><font color="#8AE234"><b>root@magento</b></font>:<font color="#729FCF"><b>~</b></font># adduser malleo --ingroup www-data --disabled-password
Adding user `malleo' ...
Adding new user `malleo' (1000) with group `www-data' ...
Creating home directory `/home/malleo' ...
Copying files from `/etc/skel' ...
Changing the user information for malleo
Enter the new value, or press ENTER for the default
Full Name []: Malleo
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] Y
<font color="#8AE234"><b>root@magento</b></font>:<font color="#729FCF"><b>~</b></font># cp ~/.ssh /home/malleo/
<font color="#8AE234"><b>root@magento</b></font>:<font color="#729FCF"><b>~</b></font># chown -R malleo:www-data /home/malleo/.ssh
<font color="#8AE234"><b>root@magento</b></font>:<font color="#729FCF"><b>~</b></font># chmod g+w -R /var/www/html
</pre>
4. Setup necessary `.ssh/config` on local machine, then connect via ssh as the new user. Or, just switch to the new user: `su <user>`.
Install Magento
---
1. Install composer (~~[steps](https://getcomposer.org/download/)~~ Magento 2.4 does not support composer 2.x)
`# apt install composer -y` (this will install Composer 1.10)
2. Get magento
- register an account at [magento marketplace](https://account.magento.com/applications/customer/create/) if you haven't already
- create [access keys](https://marketplace.magento.com/customer/accessKeys/) (public key will be used as Username, private key as Password in the following step) ([details](https://devdocs.magento.com/guides/v2.3/install-gde/prereq/connect-auth.html))
- cd to web server's docroot, use `composer` to install Magento. Provide Username and Password from previous step when prompted.
<pre><font color="#8AE234"><b>malleo@magento</b></font>:<font color="#729FCF"><b>/var/www/html</b></font>$ composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition magento2.4
<font color="#4E9A06">Creating a "magento/project-community-edition" project at "./magento2.4"</font>
<span style="background-color:#C4A000"><font color="#2E3436">Warning from repo.magento.com: You haven't provided your Magento authentication keys. For instructions, visit https://devdocs.magento.com/guides/v2.3/install-gde/prereq/connect-auth.html</font></span>
Authentication required (<font color="#4E9A06">repo.magento.com</font>):
Username: 9ad314f5ee397972****************
Password:
Do you want to store credentials for repo.magento.com in /home/malleo/.config/composer/auth.json ? [Yn] Y
<font color="#4E9A06">Installing magento/project-community-edition (2.4.1)</font>
- Installing <font color="#4E9A06">magento/project-community-edition</font> (<font color="#C4A000">2.4.1</font>): Downloading (<font color="#C4A000">100%</font>)
<font color="#4E9A06">Created project in /var/www/html/magento2.4</font>
<font color="#4E9A06">Loading composer repositories with package information</font>
<span style="background-color:#C4A000"><font color="#2E3436">Warning from https://repo.packagist.org: You are using an outdated version of Composer. Composer 2.0 is now available and you should upgrade. See https://getcomposer.org/2</font></span>
<font color="#4E9A06">Updating dependencies (including require-dev)</font>
...
<font color="#4E9A06">Writing lock file</font>
<font color="#4E9A06">Generating autoload files</font>
<font color="#4E9A06">90 packages you are using are looking for funding.</font>
<font color="#4E9A06">Use the `composer fund` command to find out more!</font>
PHP CodeSniffer Config <font color="#4E9A06">installed_paths</font> <font color="#C4A000">set to</font> <font color="#4E9A06">../../magento/magento-coding-standard,../../phpcompatibility/php-compatibility</font></pre>
- Process may get killed at <font color="#4E9A06">`Updating dependencies (including require-dev)`</font> if the machine has insufficient memory. Possible solutions: ([ref](https://stackoverflow.com/questions/20667761/composer-killed-while-updating))
- stop `mysql` temporarily: `service mysql stop`
- [add temporary swap](https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-20-04) (the swap file created does not persist through reboot)
- if it says `xxx` extension is missing, do `apt install php-xxx` as root.
3. Install Magento (using CLI)
- to run magento from anywhere, add the following line to `/etc/bash.bashrc` or `~/.bash_aliases`:
```
alias magento='/var/www/html/magento2/bin/magento'
```
- then, install magento
```bash
magento setup:install --base-url=http://188.166.223.100/magento2/ \
--db-host=localhost --db-name=magento --db-user=magento --db-password=magento \
--admin-firstname=malleo --admin-lastname=s \
--admin-email=******@gmail.com --admin-user=malleo --admin-password=********** \
--language=en_US --currency=TWD --timezone=Asia/Taipei --use-rewrites=1 \
--search-engine=elasticsearch7 \
--elasticsearch-host=$elastichost --elasticsearch-port=$elasticport \
--elasticsearch-enable-auth=1 --elasticsearch-username=$elasticuser --elasticsearch-password=$elasticpw
```
- if success, a backend URI will be given (e.g. `admin_k6iTr`)
- if failed and need to redo install, append `--cleanup-database`
4. Verify installation
- go to [website](http://188.166.223.100)

- if no style is displayed, try running `magento setup:static-content:deploy -f` then hit <kbd>ctrl</kbd>+<kbd>F5</kbd> ([ref](https://magento.stackexchange.com/questions/136879/accidentally-deleted-htaccess-file-inside-pub-static-folder))
- log into admin at `http://<your-website-address>/<admin-backend>`

- if asked to setup two-factor but the host machine does not have a mail server, do the following to disable the tfa. ([ref](https://community.magento.com/t5/Installing-Magento-2-x/An-E-mail-was-sent-to-configure-Two-Factor-Authorization-in/td-p/458322))
```
$ magento module:disable Magento_TwoFactorAuth
$ magento cache:flush
```
5. Add sample data
:::warning
**do not** add your own product, category, etc before installing sample data. db conflict may occur otherwise.
:::
use composer to install sample data, then do `setup:upgrade` to update database etc. ([ref](https://devdocs.magento.com/guides/v2.4/install-gde/install/cli/install-cli-sample-data-composer.html))
```
$ magento sampledata:deploy
$ magento setup:upgrade
```
6. cleaning up generated files:
```
$ rm -r var/cache/* var/view_preprocessed/* pub/static/adminhtml/* pub/static/frontend/*
```
7. give access to `www-data`: (need root)
```
# chmod -R g+w var pub generated
```
Module development
---
ref: [mage plaza](https://www.mageplaza.com/magento-2-module-development/), [module root directory](https://devdocs.magento.com/guides/v2.4/extension-dev-guide/prepare/prepare_file-str.html#root-directory-location), [module file structure](https://devdocs.magento.com/guides/v2.4/extension-dev-guide/build/module-file-structure.html#module-file-structure)
There is two possible module root directory:
1. `<magento root>/app`
Recommended. Works with git. Magento core will be in this directory if installed via `git clone`.
- modules: `app/code`
- themes: `app/design`
- language: `app/i18n`
2. `<magento root>/vendor`
Magento core and third-party components will be in this directory if installed via `composer create-project`. This directory is ignored by `git`.
In this example, the module `hello world` will live in `app/code/Malleo/HelloWorld`.
:::info
replace `Malleo` with your Vendor name (e.g. your name or your company name) in the following example.
:::
### 1. Create configuration file
The module's config files live in `<module root>/etc`. ([ref](https://devdocs.magento.com/guides/v2.4/extension-dev-guide/build/required-configuration-files.html#use-etc-for-your-configuration-files))
```bash
# create module's root directory and etc directory
# use switch -p to create parent directory if it does not exist
$ mkdir -p app/code/Malleo/HelloWorld/etc
```
Add the following content to `<module root>/etc/module.xml` to define the module's name.
```xml=
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Malleo_HelloWorld" setup_version="1.0.0">
</module>
</config>
```
### 2. Register the module
The module must be registered to Magento system by a `<module root>/registration.php` file, which uses `ComponentRegistrar::register();`. Add the following content to `app/code/Malleo/HelloWorld/registration.php`: ([ref](https://devdocs.magento.com/guides/v2.4/extension-dev-guide/build/component-registration.html#register-modules))
```php=
<?php
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Malleo_HelloWorld',
__DIR__);
```
Verify the module's existence by running `magento module:status`. The module's name should appear under <font color="#4E9A06">`List of disabled modules:`</font>
### 3. Create frontend route
The frontend route config file is `<module root>/etc/frontend/routes.xml`. Add the following content so that the (frontend) calls to the module will be routed to `http://<magento base url>/helloworld/...`:
```xml=
<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route frontName="helloworld" id="helloworld">
<module name="Malleo_HelloWorld"/>
</route>
</router>
</config>
```
### 4. Implement logic
(ref: [Mage Plaza](https://www.mageplaza.com/magento-2-module-development/how-to-create-controllers-magento-2.html)) Add the following to `<module root>/Controller/Index/Index.php`. This class extends `Action`, which will be returned as an instance of `ActionController` when matched by router. `FrontController` will call `dispatch()` on it, which then calls `Action::execute()`. A page will then be created by `PageFactory`.
```php=
<?php
namespace Malleo\HelloWorld\Controller\Index;
use \Magento\Framework\App\Action\Context;
use \Magento\Framework\View\Result\PageFactory;
class Index extends \Magento\Framework\App\Action\Action
{
protected $_pageFactory;
public function __construct(
Context $context,
PageFactory $pageFactory)
{
$this->_pageFactory = $pageFactory;
return parent::__construct($context);
}
public function execute()
{
return $this->_pageFactory->create();
}
}
```
### 5. Create layout
Create a simple layout file `<module root>/view/frontend/layout/helloworld_index_index.xml`, this will be used by `http://<magento base url>/helloworld/index/index`, The Block and template is defined.
```xml=
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<referenceContainer name="content">
<block class="Malleo\HelloWorld\Block\Index" name="helloworld_index" template="Malleo_HelloWorld::index.phtml" />
</referenceContainer>
</page>
```
### 6. Create the block used in layout
The block file should be `<module root>/Block/Index.php`. It has a function that tells you what day it is.
```php=
<?php
namespace Malleo\HelloWorld\Block;
class Index extends \Magento\Framework\View\Element\Template
{
public function getDayText()
{
return "Today is " . date("l") . " :)";
}
}
```
### 7. Create the template used in layout
Templates live in `<module root>/view/frontend/templates`, and the filename is `index.phtml` as defined earlier. The template displays "Hello World" as level 2 header, and calls the function of the associated Block.
```htmlmixed=
<h2>Hello World!</h2>
<p><?php echo $this->getDayText(); ?></p>
```
### 8. Enable the module
ref: [magento docs](https://devdocs.magento.com/guides/v2.4/extension-dev-guide/build/enable-module.html)
1. disable cache at `system`->`cache management` in admin
2. enable the module, upgrade db, clear cache
```bash
$ magento module:enable --clear-static-content Malleo_HelloWorld
$ magento setup:upgrade
$ magento cache:clean # or $ magento c:c
```
3. check status of module: `$ magento module:status Malleo_HelloWorld`, should give output `Module is enabled`
4. enable cache again: `$ magento cache:enable`
5. if module is modified, go to `system->cache management` and refresh cache type `Blocks HTML output` and `Page Cache`.
6. go to `http://<magento base url>/helloworld` or `http://<magento base url>/helloworld/index/index` to see module output.
