Skip to main content

Deployment

When you're ready to deploy your Formidable application to production, there are some important things you can do to make sure your application is running as efficiently as possible. In this document, we'll cover some great starting points for making sure your Formidable application is deployed properly.

Server Requirements

The Formidable framework has a few system requirements. You should ensure that your web server has the following minimum Node version:

  • Node >=18.*
  • npm/pnpm/yarn/bun

Deploy

Formidable is Heroku-ready out of the box. Here are some few things you may need to do to get started:

Create a Procfile in the root of your application with the following content:

Procfile
web: npm start
cron: node craftsman schedule:run

If your application is making use of the queue system, you can add the following line to your Procfile:

Procfile
worker: node craftsman queue:work
info

Don't forget to add production .env details to Heroku. Remember to set APP_DEBUG to false.

That's all you need to do to get started.

Nginx

If you need more control over your server and application, we recommend deploying to a Linux server and using Nginx and PM2.

Before getting started, make sure the following prerequisites are met:

Serving Your Application

Now that you have all the dependencies, you can go ahead and create a ecosystem.config.js file in the root of your application:

ecosystem.config.js
module.exports = {
apps: [
{
name: "web",
script: "npm run start",
time: true,
error_file: "./storage/logs/web/error.log",
out_file: "./storage/logs/web/log.log"
},
{
name: "cron",
script: "node craftsman schedule:run --no-ansi",
max_memory_restart: "100M",
time: true,
error_file: "./storage/logs/cron/error.log",
out_file: "./storage/logs/cron/log.log"
}
]
}

And finally, start your application:

pm2 start ecosystem.config.js

By default, this will start our application on http://127.0.0.1:3000, we can change port in the server file:

server
Server
.use(require('./.formidable/build').default)
.start({
port: 3000,
host: 'localhost'
})
info

We also recommend you enable PM2 to auto start your application on system boot. You can do this by running the command: pm2 startup

Creating a Reverse Proxy

Now that you have started your application you can go ahead and create a virtual host:

/etc/nginx/sites-available/app.conf
server {
listen 80;
server_name _;

location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.0;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

Once thats done, we can check for any issues on our newly created app.conf:

sudo nginx -t

If everything is fine, we should see a "success" message. Then we can enable our application by creating a symbolic link of the app.conf file from the /etc/nginx/sites-available/ directory to /etc/nginx/sites-enabled/:

sudo ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/

Now, for changes to reflect, you will need to restart Nginx:

sudo systemctl reload nginx

And now you should be able to access your application 🎉🎉🎉

Automating things

Its not always practical to ssh into your server to pull your latest changes. Because of this, we may wish to automate things a bit using Bash scripts.

Here's a simple bash script that pulls the latest changes from a repo and run the necessary commands:

/root/deploy.sh
echo "Jump to application folder"
cd /root/app

echo "Update application from Git"
git pull

echo "Install application dependencies"
npm install

echo "Build application"
npm run build

echo "Put application in maintenance mode"
node craftsman down

echo "Run database migrations"
node craftsman migrate:latest --no-interaction

echo "Restart application"
pm2 restart ecosystem.config.js

echo "Put application back online"
node craftsman up

This script can be triggered by a Github Action, for example. When we push to our main branch, we can have a Github Workflow that ssh's into our server on our behalf and executes the deploy.sh script:

.github/workflows/deploy.yaml
name: Deploying

on:
push:
branches:
- main

jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest

steps:
- name: executing remote ssh commands using ssh key
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USER }}
key: ${{ secrets.KEY }}
script: sh /root/deploy.sh
info

This is only an example. You don't have to use Github to be able to Automate your deployments. The same can also be achieved with Gitlab and Bitbucket Pipelines.

Docker

You can also use Docker to deploy your application. Here's a simple Dockerfile that you can use to build your application:

Dockerfile
FROM node:18-alpine

# Create app directory
RUN mkdir -p /usr/app
WORKDIR /usr/app

# Install app dependencies
COPY package.json /usr/app/
RUN npm install

# Bundle app source
COPY . /usr/app
RUN npm run build
COPY . /usr/app

EXPOSE 3000
CMD ["npm", "start"]
info

Set APP_DEBUG to false in your .env file before building your application for production.