DEV Community

Cover image for Deploy Adonis 5 in Production (with CI/CD + Github Actions)
Julien Le Coupanec
Julien Le Coupanec

Posted on • Edited on

Deploy Adonis 5 in Production (with CI/CD + Github Actions)

Deploy Adonis in Production

Let's see how we can deploy and run Adonis in production on a remote server. I will assume that you are using Ubuntu all along with this article to adapt the command for another distribution.

1. Initial Server Setup

Skip this step if your server is already set up.

sudo adduser adonis # Create a new user sudo usermod -aG sudo adonis # Grant administrative privileges sudo ufw allow OpenSSH # Make sure that the firewall allows SSH connections sudo ufw enable # Enable the firewall sudo apt-get update # Update packages sudo apt-get install nodejs npm # Install node and NPM sudo npm i -g pm2 # Install pm2 to manage node  # Install nvm to manage the node versions curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash bash # Reload bash environment nvm install 14.15.4 # Install your Node version node -v # Check node version 
Enter fullscreen mode Exit fullscreen mode

To access your server with SSH, go back to your local machine and execute the following instructions to display your public key.

cat ~/.ssh/id_rsa.pub 
Enter fullscreen mode Exit fullscreen mode

Copy the SSH key printed to the terminal and go back to your remote server.

# If you are still logged in as root, run the command below: su - adonis mkdir ~/.ssh chmod 700 ~/.ssh nano ~/.ssh/authorized_keys # Copy the public key here chmod 600 ~/.ssh/authorized_keys # Restrict the permissions of the authorized_keys file 
Enter fullscreen mode Exit fullscreen mode

2. Clone Your Adonis Repository

To allow the server to access your Adonis project on Github/Gitlab, we need to generate a new SSH key and it to your account.

# Generate a new ssh key # Follow the instructions and make sure to remember the name for the newly created key ssh-keygen -t rsa -b 2048 -C "email@example.com" # Copy the SSH key pbcopy < ~/.ssh/id_rsa.pub # MacOs xclip -sel clip < ~/.ssh/id_rsa.pub # Linux (requires the xclip package) cat ~/.ssh/id_rsa.pub | clip # Git Bash on Windows 
Enter fullscreen mode Exit fullscreen mode

Then, add this new SSH Key to Github or Gitlab. Read the article below to learn more about the exact steps.

You should now be able to clone your repository and install its dependencies.

git clone git@github.com:username/repository-name.git git checkout production # If you need to switch to a specific production branch npm install # Install dependencies 
Enter fullscreen mode Exit fullscreen mode

3. Set up Mysql

Here is how you can install MySQL on your server.

sudo apt install mysql-server systemctl status mysql # Check the MySQL is installed mysql_secure_installation # Secure MySQL (Follow the instructions) mysql -u root -p # Connect to MySQL 
Enter fullscreen mode Exit fullscreen mode

Here are the commands to run to create a new user and database for your project.

# Create a new MySQL user CREATE USER 'root'@'%' IDENTIFIED BY 'YOUR_PASSWORD'; # Replace YOUR_PASSWORD ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'YOUR_PASSWORD'; ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'YOUR_PASSWORD'; # Create a new database CREATE DATABASE database_name; # Grant the new user privileges to the tables on the new database GRANT ALL ON *.* TO 'root'@'%'; # For the changes to take effect FLUSH PRIVILEGES; # Exit the MySQL server exit 
Enter fullscreen mode Exit fullscreen mode

To allow remote access:

ufw allow 3306 sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf # Then replace 127.0.0.1 by 0.0.0.0 for bind-address bind-address = 0.0.0.0 # Restart mysql service mysql restart 
Enter fullscreen mode Exit fullscreen mode

4. Set up Redis (For Adonis Bull).

sudo apt install redis-server 
Enter fullscreen mode Exit fullscreen mode

Configure redis.

sudo nano /etc/redis/redis.conf # Inside the file, find the supervised directive. # This directive allows you to declare an init system to manage Redis as a service, providing you with more control over its operation. # The supervised directive is set to no by default. Since you are running Ubuntu, which uses the systemd init system, change this to systemd. supervised systemd # By default, Redis is only accessible from localhost. # However, if you installed and configured Redis by following a different tutorial than this one, you might have updated the configuration file to allow connections from anywhere. # This is not as secure as binding to localhost. bind 127.0.0.1 ::1 
Enter fullscreen mode Exit fullscreen mode

Check that redis is working.

sudo systemctl status redis 
Enter fullscreen mode Exit fullscreen mode

Upgrade Redis to a specific version.

# Next, download the redis tar file from https://redis.io/download, then install it from the directory it downloaded to: cd ~/ wget http://download.redis.io/releases/redis-6.0.10.tar.gz redis-6.0.10.tar.gz tar -xf redis-6.0.10.tar.gz rm redis-6.0.10.tar.gz cd redis-6.0.10 make # Next, we’ll move the new installed redis to the location where the current instance is running. sudo mv src/redis-server /usr/bin sudo mv src/redis-cli /usr/bin # After copy content you need restart redis-server: sudo /etc/init.d/redis-server restart # To validate the version of redis-server and redis-cli run: redis-cli -v #redis-cli version redis-cli INFO #redis-server version rm -rf ~/redis-6.0.10 
Enter fullscreen mode Exit fullscreen mode

5. Build for Production

Go back to your project repository and create a new .env file.

nano .env 
Enter fullscreen mode Exit fullscreen mode
npm run build # Generate the production bundle cp .env build/.env # Copy the .env file to the production bundle pm2 start build/server.js --name API # Start the Adonis server 
Enter fullscreen mode Exit fullscreen mode

You may be wondering why we need to copy the .env file to the build folder. In a nutshell, there is no standard way of defining environment variables. One could do it via a .env file, via Heroku or AWS management console's or an app specification file for Digital Ocean. Earlier (before the recent release), we used to copy the .env file. But this can lead to false-positive behavior too. This means that from now on, you must run cp .env build/.env after each build.

Then ping your server with curl to see if everything is behaving correctly.

curl 127.0.0.1:3333 # Do you get a response? 
Enter fullscreen mode Exit fullscreen mode

One final step is to run the migrations and seed the database.

node ace migration:run --force node ace db:seed --force 
Enter fullscreen mode Exit fullscreen mode

Regarding pm2, here are a few commands you should know to manage your processes.

  • pm2 kill: stop and remove all processes.
  • pm2 start command --name PROCESS_NAME: stop the process name
  • pm2 stop PROCESS_NAME: stop a given process
  • pm2 restart PROCESS_NAME: restart a given process

6. Set up Nginx with SSL.

To allow people to access Adonis from your domain name, we will install Nginx. We will also configure SSL to make sure the connection is secured.

sudo apt install nginx # Install Nginx sudo ufw allow 'Nginx HTTPS' # Open the port 443 only (TLS/SSL encrypted traffic) systemctl status nginx # Check that Nginx is running sudo apt install certbot python3-certbot-nginx # Install certbot sudo ufw disable # Disable the firewall while generating the certificate sudo certbot certonly -d api.example.com # Generate the certificate sudo ufw enable # Enable the firewall 
Enter fullscreen mode Exit fullscreen mode

Useful certbot commands

certbot certificates # List certificates certbot delete # Delete a specific certificate 
Enter fullscreen mode Exit fullscreen mode

Configure Nginx.

cd /etc/nginx/sites-available nano default 
Enter fullscreen mode Exit fullscreen mode

Here is a possible configuration file to deliver your website with SSL.

server { server_name api.example.com; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-NginX-Proxy true; proxy_pass http://127.0.0.1:3333; proxy_set_header Host $http_host; proxy_cache_bypass $http_upgrade; proxy_redirect off; } listen [::]:443 ssl ipv6only=on; listen 443 ssl; ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; } server { if ($host = api.example.com) { return 301 https://$host$request_uri; } listen 80 default_server; listen [::]:80 default_server; server_name api.example.com; return 404; } 
Enter fullscreen mode Exit fullscreen mode

Then, restart Nginx:

sudo systemctl restart nginx 
Enter fullscreen mode Exit fullscreen mode

7. Set up Your Continuous Deployment Process with Github Actions

If you are looking to deploy a new version of Adonis while merging on your production branch, here is a GitHub action file that connects to your remove server, run some instructions and notify you on Slack if something fails or succeeds.

Make sure to configure the related secret variables: secrets.HOST, secrets.USERNAME, secrets.KEY, secrets.SLACK_WEBHOOK.

name: Deploy on: push: branches: - production jobs: deploy: runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@v2 - name: Deploying uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.KEY }} script_stop: true script: | # Copy and build new version rm -rf my-project-repo-new cp -r my-project-repo my-project-repo-new cd my-project-repo-new git checkout production git reset --hard origin/production git pull npm rebuild npm install # Build the api npm run build cp .env build/.env # Run migrations node ace migration:run --force node ace db:seed --force # Replace current version with the new one cd .. mv my-project-repo my-project-repo-old mv my-project-repo-new my-project-repo # Restart server cd my-project-repo pm2 delete my-project-repo pm2 start build/server.js --name my-project-repo rm -rf ../my-project-repo-old - name: Slack success notification if: success() uses: rtCamp/action-slack-notify@master env: SLACK_CHANNEL: my-channel SLACK_COLOR: good SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png SLACK_MESSAGE: 'Deployment achieved with success' SLACK_TITLE: CI SLACK_USERNAME: GitHub SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - name: Slack error notification if: failure() uses: rtCamp/action-slack-notify@master env: SLACK_CHANNEL: my-channel SLACK_COLOR: danger SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png SLACK_MESSAGE: 'The deployment has failed @channel' SLACK_TITLE: CI SLACK_USERNAME: GitHub SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)