DEV Community

Cover image for How to: Automate Your Next.js Docker App with Render and GitHub Actions
Michelle Mendoza
Michelle Mendoza

Posted on

How to: Automate Your Next.js Docker App with Render and GitHub Actions

Serverless deployment like vercel are great for speed and ease of use but they come with limits, your functions can’t stay alive and do background work. If you want to create a simple function that will run every hour, it will be impossible without having to create a workaround which can be frustrating if you just want to implement it within your code.

The solution to these serverless limits is to deploy your application as a Docker image. Unlike serverless environments where your function will stop after execution, a Docker container runs as a persistent server, allowing you to run background functions or scheduled tasks without the risk of termination.

In my previous article, I wrote a tutorial on how to create a leaner, optimized Dockerized image in your Next.js app. While this gives you the leaner and optimized image, your next question is probably “Where do I use this now?”. There’s a lot of options to choose from like AWS which is widely known, but for someone who is just starting to learn how to connect these things, it can be overwhelming.

Which is why in this tutorial, I will introduce Render, a powerful and developer-friendly platform that's a cost-effective alternative to AWS. Its free tier is perfect for deploying and testing the exact production-level workflow that we'll build in this guide.

We will also setup our Github workflow to do these 3 things.

  1. Automatically trigger a docker image build when a branch has been merged to main (or your branch of choice)
  2. Push the docker image to docker registry when it finishes building.
  3. Lastly, when pushing to registry is successful, we will trigger a webhook from render that would automatically deploy the docker image.

This would finally automate your deployment pipeline for your Next.js app.


🚨 Prerequisites

  1. A Dockerized Next.js App: Your app must be Dockerized using the standalone output. (If you haven't done this, you can follow my guide here).
  2. A Docker Hub Account with:
    • A public repository created to host your image.
    • A Personal Access Token (PAT).
  3. A Manually Pushed Image: As a final setup step, you must manually build and push your first image to your new Docker Hub repository. This is required for Render to find and deploy your image in Step 1.

🛠️ Walkthrough

This guide is a two-part plan. First, we'll do it manually by deploying our existing Docker image to Render. This is a crucial step because Render requires a valid, existing image in order to create our new service. Second, once our service is live, we'll automate the entire process by building a complete CI/CD pipeline in GitHub Actions.

Step 1: Manually Deploying Your Image to Render

  1. Head on to render to create your account. After registering and personalization questions, you can now add a new service.
  2. Within the list, look for webservice. List of services offered by render
  3. After selecting webservice, you will see 3 options, but pick the existing image. Options to choose where the repository/image will come from
  4. This will show you the field to enter your docker image reference that you want to deploy. Ensure that your image is set to public to complete this process without adding your credentials. Once you are done, click connect.
  5. It will now allow you to set your own webservice name and pick a region and payment tier. For this tutorial, we will be using free tier. Image of the part where you can set webservice name and change tier
  6. Then below the Instance Type section is the environment variables. You can add your environment variables here if you have one. If you not, you can just skip and click deploy web service. Part where you can set your environment variables
  7. You will be redirected to the logs screen of your webservice where you will see the status of deployment. After the successful deployment, you should be able to visit your deployed app.
  8. Now that the deployment part is done, navigate to the settings. Head to the deploy section and you will see the Deploy Hook. Copy your deploy hook and save it somewhere as we will be using this later when we configure your GitHub CI/CD.

Step 2: Automating your docker image build and deploying to render

  1. After completing the setup on Render, head over to your github repository where your project is located. Go to settings/security/secrets and variables/actions. We need to add the secrets and variables so that our workflow file can use them.
    a. Create your repository secrets
    First, click on the secrets tab and select “New repository secret”. Then create the following:

    • DOCKER_PASSWORD: Paste your Docker PAT (Personal Access Token) here.
    • RENDER_WEBHOOK: Paste your Deploy Hook URL that you saved from Render.

    b. Create your repository variables
    Click the variables tab (right next to secrets) and create the following variables:

    • DOCKER_USERNAME: Your Docker Hub username.
    • DOCKER_REPO_NAME: The name of your Docker Hub repository (e.g., my-next-app).
  2. Now that the secrets and variables are in place, go to your .yml file (Or if you don’t have one, create it under .github/workflows in your root folder) and apply the following code:

    name: Build and Deploy Pipeline
    run-name: Build & Deploy run triggered by ${{ github.actor }}
    on:
    pull_request:
    types: [closed]
    branches:
      - develop
    jobs:
    build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository code
        uses: actions/checkout@v5
      - name: Extract docker image metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{vars.DOCKER_USERNAME}}/${{vars.DOCKER_REPO_NAME}}
      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ vars.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Build and push docker images
        uses: docker/build-push-action@v6
                with:
                  tags: ${{ steps.meta.outputs.tags }}
                  push: true
      - name: Notify webhook
        run: curl -f -X POST ${{ secrets.RENDER_WEBHOOK }}
    

    Here are the key takeaway of this .yml file

    • on: pull_request: In this .yml file, we have set up a condition that will only trigger once the condition is met. If the pull_request has been merged and closed in the develop branch, then the workflow will run. You are free to set up your own condition here depending on your needs.
    • Checkout repository code: Here we clone your repository in the runner’s workspace to allow it to be accessed in the next step.
    • Extract docker image metadata: In this step, we will use docker/metadata-action@v5 to generate smart image tags based on the GitHub event. For example, it will tag this build with the develop branch name, which is why our build-and-push step can just use ${{ steps.meta.outputs.tags }} without us hardcoding any tag names.
    • Login in to Docker Hub: This step securely logs into Docker Hub using the DOCKER_USERNAME and DOCKER_PASSWORD secrets we created. This is a required step before our workflow is allowed to push the new image to the registry.
    • Set up Docker Buildx / Build and push Docker images: In these steps, we configure the Docker Buildx builder and then use it to build the image and push it to the registry. The generated metadata earlier will be used here to define the image tag.
    • Notify Webhook: This is the final step that will call the webhook from render. This will allow the automatic deployment of the new image that just got pushed to the registry. Do not forget the -f flag in this step, it will notify you if reaching the webhook fails which will help for debugging.
  3. Now that everything is in place, you can test out the flow by creating a pull request and merging it to your develop. After doing that, head over to the actions tab in your GitHub repository and you should see the CI/CD steps get executed.

  4. Head over to render to verify that a deployment has been triggered and now you just need to wait for it to be completed. Once the deployment is successful, refresh your live app’s URL to see your new changes in production!


And there you have it 🎉 You have successfully automated the build process that would normally take 3 or 4 manual steps to do it. By deploying your Next.js app on a proper server, you can now create functions that can run and operate on intervals without the worry that it will stop.

How did your deployment go? Did you get your pipeline up and running? If you hit any issues or have a pro-tip to share, drop it in the comments below!

Top comments (0)