Liam Hampton

In this blog-tutorial you will:

  • Use GitHub Codespaces (or your own IDE)
  • Create a simple Go web server application
  • Run unit tests on the application
  • Build the application into a multi-stage Docker Image
  • Use the Azure Container Registry (ACR) to store your Docker Images
  • Deploy the application into Azure App Services
  • Automate the delivery pipeline utilising GitHub Actions

Note Using GitHub Codespaces can incur costs. At the time of writing this tutorial, GitHub free accounts get 120 core-hours of Codespaces compute and 15GB of Codespaces storage for free. Please do consult the GitHub Codespaces pricing page for the most up to date information.

This application will be built entirely from within your browser. To do this, you will be using the items listed below in the prerequisites. If you want to run this locally using VSCode or another IDE, please also install the items marked as “Optional” in the list below.


Example Code Repository

Build, Test and Deploy to Azure


  • You are using the example code repository
  • You are using Codespaces to open and run the application


This hands-on tutorial provides you with an existing Go web application that you can build upon. To use the existing code repository click on the “Build, Test and Deploy to Azure” link above and this will open the GitHub repository in your browser. Click the “Fork” button in the top right-hand corner of the screen and this will create a copy of the repository in your GitHub account.

fork button

The repository contains a Go web server that uses handlebars templates, basic unit tests and an Azure CLI Codespaces image configuration in the .devcontainer directory. A Dockerfile is also included that creates a multi-stage Docker Image, along with a GitHub Actions workflow to build, test, and deploy the app to Azure App Services.

Note We are using Devcontainers as this provides a clean and consistent experience for everyone. Devcontainers are designed for ease of use and allow you to develop complex solutions from within your browser window.


Navigate to your fork of the example code repository and click on the Code button and select Codespaces on the top ribbon. Now click on the button Create codespace on main and wait for the Codespace to be created (this could take a minute or two).

codespaces button

codespaces setup

Once Codespaces has been created, you will be able to see the application and all the files in the Explorer tab on the left-hand side of the screen. You can also open a new terminal by clicking on the + button in the terminal tab, however, one should open automatically.

codespaces environment

Before going any further, test the application is running by running the following command in the terminal:

go run main.go

You should see the following output:

 │                   Fiber v2.42.0                   │ 
 │                    │ 
 │       (bound on host and port 3000)       │ 
 │                                                   │ 
 │ Handlers ............. 2  Processes ........... 1 │ 
 │ Prefork ....... Disabled  PID .............. 6209 │ 

This output indicates the program is running and listening on port 3000. You should now see a new port within the Ports terminal tab and see the application running in Codespaces. Click on the local address or the green popup button “Open in Browser” to open the running application in the browser.

ports tab

To close the server down, press Ctrl + C in the terminal.

Now you have seen the application running, it is time to run the tests. To do this, navigate to the root project directory and run the following command in the terminal:

go test ./... -v

You should see the following output:

=== RUN   Test_filesExist
--- PASS: Test_filesExist (0.00s)
ok       0.005s

This output indicates that the test file has run successfully. The tests are only checking for important files to be present but you can add your own tests to the main_test.go file to test your application code as it grows.

The a multi-stage Docker Image

Lets turn our attention to the root of the project where you will find a Dockerfile that builds the application into a multi-stage Docker Image. This is a best practice that is followed as it reduces the size of the image and therefore reduces the time it takes to push/pull the image to/from the registry.

If you do not use a multi-stage Docker Image, this image will be ~350mb compared to using a multi-stage build resulting in the image being ~15mb. This is a huge difference and will help with data transfer costs, storage costs and speed up the deployment process. The Dockerfile is as follows:

FROM golang:1.19.5-alpine3.17 as build
COPY . .
RUN go mod download
RUN GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o website
RUN chmod +x website
FROM alpine:3.17
COPY --from=build go/src/app/views /views
COPY --from=build go/src/app/website /
ENTRYPOINT [ "/website" ]

Creating a new Azure Container Registry (ACR)

The next step is to set up an Azure Container Registry (ACR) to store the build assets. You can also use alternative registries such as Docker Hub, but ACR is being used in this tutorial.

Note You need to change myResourceGroup and myregistry to something more meaningful to you. For example, deploytestresourcegroup and deploytestregistry. Please ensure you are changing it throughout all commands in this tutorial.

Step 1 – Open a new terminal in Codespaces and run the following command and follow the prompts to login to your Azure Account:

az login --use-device-code

Step 2 – To create a new Azure Container Registry (ACR) and enable admin access for deployment, run the following commands:

// Create a new resource group
az group create --name myResourceGroup --location westeurope
// Create a new ACR
az acr create --resource-group myResourceGroup --name myregistry --sku Basic
// Enable admin access
az acr update --name myregistry --admin-enabled true

Step 3 – login to the ACR using the following command:

az acr login --name myregistry

image in azure

Build and push Docker Image to ACR

Once the ACR has been created, the Docker Image can be built and pushed to the registry. The docker commands used here can be run locally (if you have Docker installed) or in Codespaces as a Docker client is provided.

Note You can change the name of the image, devops-container to something more meaningful to you but be sure to change it in all commands.

Step 1 – Build the Docker Image using the following command:

docker build --platform linux/amd64 --tag .

Step 2 – Check the image has been built

docker images

Step 3 – tag the image using the following command:

docker tag

Repeat step 2 to check the image has been tagged correctly.

Step 4 – push the image to the ACR using the following command:

docker push

Deploy application to Azure App Services

Step 1 – Create a new App Service Plan using the following command:

az appservice plan create --name myAppServicePlan --resource-group myResourceGroup --sku B1 --is-linux

Step 2 – Create a new App Service using the following command:

az webapp create --resource-group myResourceGroup --plan myAppServicePlan --name myAppService --deployment-container-image-name

Note If you see this error in the terminal when you run the command above, then you need to chose a different name for your application: “Webapp ‘myAppService’ already exists. The command will use the existing app’s settings. Unable to retrieve details of the existing app ‘myAppService’. Please check that the app is a part of the current subscription”

Step 3 – Open the App Service in a browser using the following command:

az webapp browse --resource-group myResourceGroup --name myAppService

Warning The app may take a few minutes to start up due to infrastructure being used in this tutorial. If you see an application error, wait a few minutes and reload the page. You can also check the logs in the Azure Portal to see if the application has started.

application error

app logs

app running in browser

Create a GitHub Actions Workflow

Now the application has been deployed to App Services, we can create a GitHub Actions Workflow. The workflow is our continuous integration and continuous deployment (CI/CD) pipeline that will build, test and automatically deploy the application to Azure.

In the project root directory there is a .github directory that contains a workflows directory. In the workflows directory there is a deployment.yml file that contains the GitHub Actions workflow. The code is written in YAML and is whitespace-sensitive so be sure to bare this in mind if you edit the file.

GitHub Actions workflow

The workflow is as follows:

name: Build a Docker image and push it to ACR
    branches: [main]
  TAG: v1
  IMAGE: devops-container
    runs-on: ubuntu-latest
      - uses: actions/checkout@v2
      - name: Set up Go
        uses: actions/setup-go@v2
          go-version: 1.19.5
      - name: Test
        run: go test -v ./...
      - uses: azure/docker-login@v1
          login-server: ${{ env.SERVER }}
          username: ${{ secrets.ACR_USERNAME }}
          password: ${{ secrets.ACR_PASSWORD }}
      - run: |
          docker build . -t ${{ env.SERVER }}/${{ env.IMAGE }}:${{ env.TAG }}
          docker push ${{ env.SERVER }}/${{ env.IMAGE }}:${{ env.TAG }}

Note Make sure to change the SEVER, IMAGE and TAG values to the same name you changed these too in earlier steps.

This workflow will: – Run on every push to the main branch (or when manually triggered) – Use the latest Ubuntu image – Checkout the repository – Setup Go – Run the repository tests – Login to the ACR – Build the Docker Image and push it to the ACR

Now the workflow is created, we need to add the ACR username and password as secrets to the repository. To do this, go to the repository Settings and click on Secrets and variables in the left-hand menu. In this drop-down list click on Actions and then click the green button New repository secret. This allows you to store sensitive information in the repository without it being exposed.

environment variables

Add the following secrets:


The values for these secrets can be found in the Azure Portal under the ACR resource or alternatively, you can use the Azure CLI to get the values by running the following command in the terminal:

az acr credential show --name myregistry

credentials in azure

Make a change to the application

After successfully deploying and running the application, you will need to enable continuous deployment for the App Service application in the Azure portal by toggling a switch.

To do this, go to the deployed App Service resource in the Azure Portal and click on Deployment Center in the left-hand menu. In the Settings blade, scroll to the bottom of the page and turn on Continuous deployment and then click on Save.

app service continuous deployment

Now that the workflow is created, it will trigger when there is a change to your main branch, optionally you can manually trigger the workflow. This will deploy changes to your application deployed in Azure.

To test this, go to your application code in your Codespaces environment and create a new branch and make a change to the main.go file. For example, change the heading title. Save the file and commit and push the change to the repository. Create a Pull Request from the development branch to the main branch on your fork and merge the Pull Request into the main branch.

Alternatively, if you have not created a development branch, you can commit the change directly to the main branch. Either method will trigger the workflow and deploy the changes to the application in Azure App Services.

Give it a go!


This post has encapsulated the core aspects of the DevOps lifecycle and has shown how we can use GitHub Codespaces, GitHub Actions, Azure Container Registry and Azure App Services to create a continuous delivery pipeline that will automatically deploy our application to Azure when a change is made.

Useful resources

Microsoft Learn Modules:

YouTube Channels: