In this blog post, we will cover the process deploying a pre-existing Nuxt3 SPA (Single Page Application) on a server using GitHub Actions, Docker, Nginx, and Traefik.
By following these steps, you'll be able to set up continuous integration and deployment for your Nuxt3 SPA, as well as host your static content on Nginx with the help of Docker and Traefik.
The main aim of this article is to show the process concerning the deployment with concrete examples of how to use DevOps tools. This article won't cover the process of creating a Nuxt3 SPA project. If you want to learn how to create a Nuxt3 SPA project, you can go to the Nuxt3 documentation to learn how to create a Nuxt3 project.
Prerequisites
- A Nuxt3 SPA project
- Basic knowledge of Docker, Nginx, and GitHub Actions
- A GitHub account
- A server with Docker and Docker Compose installed
- Domain name and access to DNS settings
I assume that you already have your Nuxt3 SPA project and you have already deployed it to GitHub.
Configuring GitHub Actions for CI/CD
What is Github Actions?
GitHub Actions is a feature provided by GitHub that allows you to automate, customize, and execute your software development workflows directly within your GitHub repository. With GitHub Actions, you can create and configure workflows that automatically respond to events like pushes, pull requests, and issue creation.
These workflows are made up of a series of tasks called "actions." Actions can be created and shared by the community, or you can write your own custom actions. And this is why Github Actions is so powerful. Workflows are defined using YAML files and can be triggered by various GitHub events.
Create your Github Actions Workflow
You need to create a new file in the .github/workflows
directory of your project named main.yml
with
the following content:
name: Deploy
on:
push:
branches:
- main
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Install dependencies
uses: borales/actions-yarn@v4
with:
cmd: install
- name: Install sass
uses: borales/actions-yarn@v4
with:
cmd: add -D sass
- name: Run generate
uses: borales/actions-yarn@v4
with:
cmd: generate
- name : Deploy to server
uses: garygrossgarten/github-action-scp@release
with:
local: .output/public
remote: /srv/config/portfolio/generated
host: ${{ secrets.HOST }}
username: ${{ secrets.SSH_USER }}
privateKey : ${{ secrets.SSH_KEY }}
This workflow will be triggered when you push to the main
branch.
The workflow will run on the latest version of Ubuntu and will perform the following steps:
- Checkout the code from the repository. This action is helpful in order to get the code from the repository and let the workflow use it.
- Install Node.js 16.x
- Install dependencies
- Install sass
- Generate the static content of the Nuxt3 SPA using the
yarn generate
command. - Deploy the static content to the server using SSH and SCP. This action will use GitHub secrets to connect to the server and
copy the static content to the server path
/srv/config/portfolio/generated
. You can use any path you want.
Create GitHub Secrets
You need to create two GitHub secrets to connect to the server using SSH and SCP. So go to your GitHub repository and click on Settings
> Secrets and variables
> Actions
and add your secrets:
SSH_KEY
: The private key of your SSH key pair.SSH_USER
: The username of your SSH key pair.HOST
: The IP address of your server.
Then push your changes to the main
branch and wait for the workflow to finish.
It should take a few minutes to complete the workflow. If everything goes well, you should see a green checkmark next to the commit.
Now, you can go to your server and check if the static content has been deployed successfully under the path /srv/config/portfolio/generated
.
Okay, now we have the static content of our Nuxt3 SPA deployed on the server. But we still need to serve it with Nginx and Traefik !
Configuring Docker, Nginx, and Traefik
Prerequisites
- A server with Docker and Docker Compose installed
- Domain name and access to DNS settings. You can configure this step with Cloudflare or any other DNS provider.
In my case, I use Cloudflare and I have already configured my domain name
williamburillon.com
with it in order to point it to my server.
Configure Traefik
Traefik is a modern, dynamic, and open-source reverse proxy and load balancer designed to simplify the deployment and management of microservices and container-based applications. It is particularly popular for use with container orchestration platforms like Docker, Kubernetes, and Swarm, as it can automatically discover and configure itself based on the services deployed in the environment.
In this section, we will configure Traefik for our servers and I will omit specific details about Traefik configuration.
You can find more information about Traefik configuration in the official documentation.
So, to configure Traefik for your server, you need to create a docker-compose file named traefik.yml
with the following content:
version: '3.7'
volumes:
traefik-certs:
networks:
frontend: {external: true}
services:
reverse-proxy:
image: traefik:v2.5.2
command:
- "--accesslog=true"
- "--log.level=DEBUG"
- "--api=true"
- "--api.dashboard=true"
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=frontend"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.cloudflarednschallenge.acme.dnschallenge=true"
- "--certificatesresolvers.cloudflarednschallenge.acme.dnschallenge.provider=cloudflare"
- "--certificatesresolvers.cloudflarednschallenge.acme.email=<YOUR_CLOUDFLARE_EMAIL>"
- "--certificatesresolvers.cloudflarednschallenge.acme.storage=/letsencrypt/acme.json"
ports:
- 80:80
- 443:443
volumes:
- traefik-certs:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
CF_API_EMAIL: <YOUR_CLOUDFLARE_EMAIL>
CF_API_KEY: <YOUR_CLOUDFLARE_API_KEY>
networks:
- frontend
labels:
- "traefik.enable=true"
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.routers.api.tls.certresolver=cloudflarednschallenge"
- "traefik.http.routers.api.rule=Host(`traefik.<YOUR_DOMAINE_NAME>`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.service=api@internal"
- "traefik.http.services.dummy-svc.loadbalancer.server.port=9999"
- "traefik.http.middlewares.traefik-dashboard-auth.basicauth.users=<YOUR_DEFINED_USERNAME>:<YOUR_DEFINED_PASSWORD>"
- "traefik.http.routers.api.middlewares=traefik-dashboard-auth"
Then, run the following command to start Traefik:
docker-compose -f traefik.yml up -d
This configuration will start Traefik and it will listen on port 80 and 443. So, each time your sever receives a request on port 80 or 443, it will forward the request to Traefik. Then, Traefik will route the request to the appropriate service. We will see how to configure Traefik to route the request to our Nuxt3 SPA in the next section.
N.B.: You can now access the Traefik dashboard at https://traefik.<YOUR_DOMAINE_NAME>
and you should see the dashboard.
Configure Nginx
Nginx (pronounced "engine-x") is a versatile, high-performance, open-source web server, reverse proxy server, and load balancer. It was first released in 2004 by Igor Sysoev, and it has gained popularity due to its performance, stability, and low resource consumption compared to other web servers like Apache.
In our case, we will use Nginx in order to serve our static content generated by Nuxt3 under the path /srv/config/portfolio/generated
.
So, to configure Nginx for our server, we need to create a docker-compose file named nginx.yml
under the path /srv/config/portfolio
with the following content:
version: "3.4"
networks:
frontend:
external: true
services:
nginx:
image: nginx:latest
restart: always
volumes:
- ./generated:/usr/share/nginx/html/
labels:
- "traefik.enable=true"
- "traefik.http.routers.portfolio.entrypoints=websecure"
- "traefik.http.routers.portfolio.rule=Host(`<YOUR_DOMAINE_NAME>`)"
- "traefik.http.routers.portfolio.tls.certresolver=cloudflarednschallenge"
- "traefik.http.services.portfolio.loadbalancer.server.port=80"
networks: [frontend]
Then, run the following command to start Nginx:
docker-compose -f nginx.yml up -d
With this configuration, we set up Nginx to serve our static content generated by Nuxt3 under the path /srv/config/portfolio/generated
(with the volume ./generated:/usr/share/nginx/html/
).
We enable Traefik for this service and we configure Traefik to route the request to the appropriate service.
We defined a router named portfolio
and we configured it to listen on port 443.
We also defined a rule to route the request to the service portfolio
(with the label traefik.http.routers.portfolio.rule=Host(<YOUR_DOMAINE_NAME>)
).
So, each time Traefik receives a request on port 443 with the host <YOUR_DOMAINE_NAME>
, it will route the request to the service portfolio
(with the label traefik.http.services.portfolio.loadbalancer.server.port=80
).
Well Done 🎉
You can now access your Nuxt3 SPA at https://<YOUR_DOMAINE_NAME>
and you should see the website.
To conclude, we have explored how to deploy a pre-existing Nuxt3 SPA using GitHub Actions for CI/CD, along with Docker, Nginx, and Traefik for hosting and managing static content. By following these steps, you can set up a robust and automated deployment pipeline for your own Nuxt3 SPA, ensuring that your content is always up-to-date and easily accessible to your audience.
If you want to deploy your Nuxt 3 app and you don't have server yet or you don't want to manage your server, you can use Vercel to deploy your app in a few minutes.
If you have any question concerning the process or if you have any suggestion, feel free to contact me by email !