• Blog
  • Docs
  • Careers
  • Get Support
  • Contact Sales
DigitalOcean
  • Featured AI Products

    Compute

    Build, deploy, and scale cloud compute resources

    Containers and Images

    Safely store and manage containers and backups

    Managed Databases

    Fully managed resources running popular database engines

    Management and Dev Tools

    Control infrastructure and gather insights

    Networking

    Secure and control traffic to apps

    Security

    Help protect your account and resources with these security features

    Storage

    Store and access any amount of data reliably in the cloud

    Browse all products

  • AI/ML

    CMS

    Data and IoT

    Developer Tools

    Gaming and Media

    Hosting

    Security and Networking

    Startups and SMBs

    Web and App Platforms

    See all solutions

  • Community

    Documentation

    Developer Tools

    Get Involved

    Utilities and Help

  • Become a Partner

    Marketplace

  • Pricing
  • Log in
  • Sign up
  • Log in
  • Sign up

Company

  • About
  • Leadership
  • Blog
  • Careers
  • Customers
  • Partners
  • Referral Program
  • Affiliate Program
  • Press
  • Legal
  • Privacy Policy
  • Security
  • Investor Relations

Products

  • Knowledge Bases
  • GPU Droplets
  • Bare Metal GPUs
  • Inference Engine
  • Data & Learning
  • Evaluations
  • Model Library
  • Droplets
  • Kubernetes
  • Functions
  • App Platform
  • Load Balancers
  • Managed Databases
  • Spaces
  • Block Storage
  • Network File Storage
  • API
  • Uptime
  • Cloud Security Posture Management (CSPM)
  • Identity and Access Management (IAM)
  • Cloudways
  • View all Products

Resources

  • Community Tutorials
  • Community Q&A
  • CSS-Tricks
  • Write for DOnations
  • Currents Research
  • DigitalOcean Startups
  • Wavemakers Program
  • Compass Council
  • Open Source
  • Newsletter Signup
  • Marketplace
  • Pricing
  • Pricing Calculator
  • Documentation
  • Release Notes
  • Code of Conduct
  • Shop Swag

Solutions

  • AI Training GPU
  • GPU Inference
  • VPS Hosting
  • Website Hosting
  • VPN
  • Docker Hosting
  • Node.js Hosting
  • Web Mobile Apps
  • WordPress Hosting
  • Virtual Machines
  • View all Solutions

Contact

  • Support
  • Sales
  • Report Abuse
  • System Status
  • Share your ideas

Company

  • About
  • Leadership
  • Blog
  • Careers
  • Customers
  • Partners
  • Referral Program
  • Affiliate Program
  • Press
  • Legal
  • Privacy Policy
  • Security
  • Investor Relations

Products

  • Knowledge Bases
  • GPU Droplets
  • Bare Metal GPUs
  • Inference Engine
  • Data & Learning
  • Evaluations
  • Model Library
  • Droplets
  • Kubernetes
  • Functions
  • App Platform
  • Load Balancers
  • Managed Databases
  • Spaces
  • Block Storage
  • Network File Storage
  • API
  • Uptime
  • Cloud Security Posture Management (CSPM)
  • Identity and Access Management (IAM)
  • Cloudways
  • View all Products

Resources

  • Community Tutorials
  • Community Q&A
  • CSS-Tricks
  • Write for DOnations
  • Currents Research
  • DigitalOcean Startups
  • Wavemakers Program
  • Compass Council
  • Open Source
  • Newsletter Signup
  • Marketplace
  • Pricing
  • Pricing Calculator
  • Documentation
  • Release Notes
  • Code of Conduct
  • Shop Swag

Solutions

  • AI Training GPU
  • GPU Inference
  • VPS Hosting
  • Website Hosting
  • VPN
  • Docker Hosting
  • Node.js Hosting
  • Web Mobile Apps
  • WordPress Hosting
  • Virtual Machines
  • View all Solutions

Contact

  • Support
  • Sales
  • Report Abuse
  • System Status
  • Share your ideas
© 2026 DigitalOcean, LLC.Sitemap.
Engineering

How to Deploy to DigitalOcean Kubernetes with GitHub Actions

author

By Andrew Starr-Bochicchio

  • Published: April 24, 2019
  • 8 min read
<- Back to blog home

Update - September 9, 2019:

The examples in this blog post use the HCL syntax used in the initial version of GitHub Actions. GitHub Actions v2 now uses a new YAML syntax. You can find an updated workflow using the new syntax in the in this example repository.

Introduction

GitHub Actions were one of the most exciting things launched by our friends at GitHub last year. Now that they’re in public beta, people are using them to build awesome stuff, from running tests and linters to more lighthearted use cases. With the DigitalOcean doctl Action, you can interact with all of your DigitalOcean resources.

One of the most powerful aspects of GitHub Actions is the ability to compose workflows using multiple Actions to accomplish complicated tasks. In this post, we’ll show what that looks like in practice.

Using multiple Actions, including ones for DigitalOcean and Docker, we’ll build a simple continuous delivery pipeline that deploys an application to a DigitalOcean Kubernetes cluster on push to the master branch of a GitHub repository. Along the way, we’ll dig into some of the details of working with GitHub Actions.

Creating Your Workflow

The first step in using GitHub Actions is to create a workflow. You can do this from the Actions tab of your GitHub repository. This is where you define what will trigger a run of your workflow.

Nearly any GitHub event can be used from a new PR being opened to a new release being tagged. In our example, we’ll be using the “push” event so that our workflow is executed when a new commit is pushed to the master branch.

This will create a new file in your repository at .github/main.workflow with the following contents:

workflow "New workflow" {  
  on = "push"  
}

This highlights an important aspect of GitHub Actions. While workflows can be created and edited using the GitHub GUI, they are configured in code using HCL – the same format used by tools like [HashiCorp’s Terraform](https://www.terraform.io/). Each change made in the GUI is mirrored in the file and will be committed to the repository. This allows you to edit your workflows offline and collaborate on them via pull requests. For the rest of this post, we’ll mostly be showing the examples as code so that it is easier to see the details of how all the pieces fit together.

Defining Your First Action

Our repository contains a Dockerfile in its root directory that defines how to build and run our application. In order to keep our example simple and focused on the workflow rather than the details of the application, our “application” is just a static site served by NGINX. The first Action block that we’ll define will build a container image from this Dockerfile:

action "Build Docker image" {  
  uses = "actions/docker/cli@master"  
  args = \["build", "-t", "andrewsomething/static-example:$(echo $GITHUB\_SHA | head -c7)", "."\]  
}

The first line is just a label for the block; the interesting bits are inside. The uses line specifies the Action that will be run. The path used to reference the Action matches its location on GitHub. For instance, here we are using the Docker CLI Action which can be found in the cli/ directory of the github.com/actions/docker repository. This Action is a wrapper around the same Docker CLI tool that you would use on the command line locally.

If you have ever built a Docker image, the next line should look familiar. The args line is just what it sounds like. Here we can pass arguments to the Docker command needed to build the image.

When we build the image, we are tagging it so that we can push it to Docker Hub. If you are following along, make sure to replace “andrewsomething” with your own username. You probably noticed that we are using the $GITHUB_SHA environment variable as part of the tag. Its value is the SHA of the commit that triggered the workflow. It is one of a number of variables made available in the Action’s runtime environment.

Using Secrets

Often you will need to store secrets that your Action will require in order to run. Our next Action block demonstrates this. To push the image we built to Docker Hub, we will first need to log in. Using the secrets line of an Action block, we can securely pass the needed information as environment variables:

action "Docker Login" {    
  uses = "actions/docker/login@master"  
  secrets = \["DOCKER\_USERNAME", "DOCKER\_PASSWORD"\]  
}

The contents of these secrets can be configured in the GitHub GUI:

While we’re here, we will also specify a DIGITALOCEAN_ACCESS_TOKEN secret using a personal access token generated from the API section of the DigitalOcean Control Panel. We’ll be using this in a later step.

Specifying Dependencies

In the next step of our workflow, we’ll push the Docker image to Docker Hub. This looks similar to the previous Action blocks, but this time we have a new line:

action "Push image to Docker Hub" {    
  needs = \["Docker Login", "Build Docker image"\]  
  uses = "actions/docker/cli@master"  
  args = \["push", "andrewsomething/static-example"\]  
}

Multiple Action blocks may run in parallel. In this case, we need to ensure that the Docker image has been built and that we have logged into Docker Hub before we can push it there. So we’ve specified a needs line referencing the labels for those two Action blocks so that they will be executed in the correct order.

Accessing Your Workspace

The config directory of our repository contains a Kubernetes YAML file specifying our deployment. As committed in git, there is only a placeholder for the Docker image that we want to deploy. It will need to be updated to point to the image we’ve tagged and pushed to Docker Hub. To do this, we’ll use the Shell Action provided by GitHub. Based on Debian, it includes all the standard UNIX tools you’d expect. Here we’re using sed to update the contents of our deployment file:

action "Update deployment file" {    
  needs = \["Push image to Docker Hub"\]  
  uses = "actions/bin/sh@master"  
  args = \["TAG=$(echo $GITHUB\_SHA | head -c7) && sed -i 's|<IMAGE>|andrewsomething/static-example:'${TAG}'|' $GITHUB\_WORKSPACE/config/deployment.yml"\]  
}

This demonstrates another important environment variable available to you, $GITHUB_WORKSPACE. This directory contains a copy of the repository that triggered the workflow. Changes made here will persist from one step to the next.

Deploying to DigitalOcean Kubernetes

In our next step, we’ll retrieve the credentials needed to access our Kuberenetes cluster using the DigitalOcean doctl Action. This Action enables you to use any doctl sub-command just like from the command line giving you access to all of your DigitalOcean resources. Using the DIGITALOCEAN_ACCESS_TOKEN secret we configured earlier, we will save the kubeconfig file for our cluster:

action "Save DigitalOcean kubeconfig" {    
  uses = "digitalocean/action-doctl@master"  
  secrets = \["DIGITALOCEAN\_ACCESS\_TOKEN"\]  
  args = \["kubernetes cluster kubeconfig show actions-example > $HOME/.kubeconfig"\]  
}

Next, we’ll configure an Action block using kubectl to apply the actual deployment:

action "Deploy to DigitalOcean Kubernetes" {    
  needs = \["Save DigitalOcean kubeconfig", "Update deployment file"\]  
  uses = "docker://lachlanevenson/k8s-kubectl"  
  runs = "sh -l -c"  
  args = \["kubectl --kubeconfig=$HOME/.kubeconfig apply -f $GITHUB\_WORKSPACE/config/deployment.yml"\]  
}

You’ll notice something new in this block demonstrating just how flexible GitHub Actions can be. In this case, the uses line is not specifying an Action on GitHub like our previous steps. Instead, it is referencing a container image hosted on DockerHub. This opens up a whole world of tools not packaged as Actions for use in your workflow.

Verifying the Deployment

In the final step of our workflow, using the same kubectl Docker image, we will check on the status of our deployment. The kubectl rollout status command returns a zero exit code when a deployment was successful:

action "Verify deployment" {    
  needs = \["Deploy to DigitalOcean Kubernetes"\]  
  uses = "docker://lachlanevenson/k8s-kubectl"  
  runs = "sh -l -c"  
  args = \["kubectl --kubeconfig=$HOME/.kubeconfig rollout status deployment/static-example"\]  
}

If the deployment fails, it returns a non-zero exit code. So that the status of our workflow will correctly reflect whether or not our application was successfully deployed, we will return to our workflow block from the first step and add a new resolves line:

workflow "New workflow" {    
  on = "push"  
  resolves = \["Verify deployment"\]  
}

Since our “Verify deployment” Action depends on all of our other Actions, we can specify it here alone. If our workflow contained completely independent Actions, we’d want to include each of them here.

Bringing It All Together

Now that we’ve successfully configured our workflow, each time a commit is pushed to the master branch of our repository it will be triggered. Each step will run in the order that we specified. The GitHub GUI will display the progress:

With everything green, our site is now live: https://doctl-action.do-api.dev/

You can find the complete workflow file with the full end-to-end example on GitHub.

Next Steps

GitHub Actions allow you to craft powerful workflows integrating multiple Actions to accomplish complicated tasks. In this post, we’ve only scratched the surface of what they can do. With the doctl Action, you can incorporate your DigitalOcean resources into your workflows. Here are a few resources to help you get started building your own:

  • Check out the source for the DigitalOcean doctl Action on GitHub.
  • Dig into Actions in more detail with GitHub’s Actions Documentation.
  • Run your GitHub Actions locally with act. Great for debugging!

In this post we mostly focused on the GitHub Actions side of the equation. If you’re looking for more info on working with Kubernetes, the DigitalOcean Kubernetes Resource Center is a great place to start.

We’d love to know how you are using GitHub Actions. So let us know in the comments below! Are there other Actions for DigitalOcean that you’d like to see? Share your feedback and requests by opening an issue on GitHub.

About the author

Andrew Starr-Bochicchio
Andrew Starr-Bochicchio
Author

Share

  • Engineering

Start building today

From GPU-powered inference and Kubernetes to managed databases and storage, get everything you need to build, scale, and deploy intelligent applications.
Sign up

Related Articles

The Inference Alpha: Maximizing Frontier Models on AMD
Engineering

The Inference Alpha: Maximizing Frontier Models on AMD

Balaji Varadarajan

  • June 10, 2026
  • 12 min read

Read more

The Inference Tax: How Prefix-Aware Routing Eliminates the Hidden Cost of LLMs at Scale
Engineering

The Inference Tax: How Prefix-Aware Routing Eliminates the Hidden Cost of LLMs at Scale

Piyush Srivastava
  • June 1, 2026
  • 13 min read

Read more

DigitalOcean Serverless Inference: A Deep Dive
Engineering

DigitalOcean Serverless Inference: A Deep Dive

smehta
  • June 1, 2026
  • 17 min read

Read more