Automating Infrastructure: Deploying to DigitalOcean via GitHub Actions

Deploying applications from a development environment to a production server often introduces friction, manual errors, and downtime. By leveraging GitHub Actions, developers can create a fully automated pipeline that bridges code repositories with DigitalOcean infrastructure. This process eliminates the need for manual file transfers or SSH-based deployments, ensuring that every push to a repository triggers a consistent, repeatable deployment sequence. Whether targeting a raw Ubuntu Droplet for maximum control or the managed App Platform for streamlined operations, the underlying principle remains the same: automation through secure, token-based authentication and scripted execution.

The integration between GitHub Actions and DigitalOcean supports two distinct architectural approaches. The first involves deploying to a Droplet, a virtual private server where the user manages the operating system, runtime environments, and application servers. This method requires configuring SSH access, managing user permissions, and handling Docker containers manually on the remote server. The second approach utilizes DigitalOcean’s App Platform, a Platform-as-a-Service (PaaS) that abstracts away infrastructure management. Here, GitHub Actions interacts directly with DigitalOcean’s API to build, deploy, and scale applications based on source code or container images. Understanding the specific requirements, security configurations, and workflow structures for both methods is essential for robust deployment strategies.

Configuring SSH Access for Droplet Deployments

Deploying to a DigitalOcean Droplet relies heavily on Secure Shell (SSH) connectivity. The GitHub Actions runner, which executes the deployment workflow, must be able to authenticate with the remote server. This requires meticulous configuration of user accounts, SSH keys, and server-side settings on the Ubuntu-based Droplet. The process begins with ensuring that the Droplet itself is properly configured to accept SSH connections and that a dedicated user account exists for the deployment agent.

On the DigitalOcean Droplet, typically running Ubuntu 20.04 or later, the initial setup involves modifying the SSH daemon configuration. The administrator must access the server via the root account and edit the /etc/ssh/sshd_config file. Within this configuration file, the PasswordAuthentication directive must be changed from no to yes. This change allows the system to process authentication requests, although key-based authentication is preferred for security. After saving the changes, the SSH service must be restarted using the command sudo systemctl restart ssh to apply the new configuration.

Creating a dedicated user for the deployment process is a critical security practice. Running deployments as the root user poses significant risks, including potential system-wide damage from erroneous commands. Instead, a new user, such as deployer or ghactions, should be created. For the deployer user, the setup involves adding the user to the system and granting them sudo privileges to perform administrative tasks if necessary. The commands adduser deployer followed by usermod -aG sudo deployer achieve this. After exiting the root session, the administrator logs in as the new user to configure SSH keys locally on the Droplet.

Generating SSH keys on the Droplet is done using the ssh-keygen command. This creates a private and public key pair. To facilitate seamless communication with GitHub, the Droplet must also recognize GitHub’s SSH fingerprint. This is accomplished by running ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts. The public key, located in ~/.ssh/id_rsa.pub, must then be added to the authorized keys files to permit passwordless login for this user. The commands cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys and cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys2 append the key. Proper file permissions are crucial for SSH security; the authorized_keys file should have permissions set to 700, and authorized_keys2 to 640, using the chmod command.

Managing GitHub Secrets and Workflow Triggers

Once the server-side configuration is complete, the focus shifts to GitHub. The deployment workflow requires sensitive information—such as the Droplet’s IP address, the username, and the private SSH key—to be stored securely. GitHub Secrets provide this functionality, ensuring that credentials are not exposed in the repository’s code history. The private key generated on the Droplet, found in ~/.ssh/id_rsa, must be copied in its entirety and stored in the repository’s secrets.

In the GitHub repository settings, under the "Secrets and Variables" section for Actions, three specific secrets should be created:
- HOST: The IP address of the DigitalOcean Droplet.
- USERNAME: The username of the deployment user (e.g., deployer or ghactions).
- KEY: The full content of the private SSH key (id_rsa).

With secrets in place, the GitHub Actions workflow file can be created. This file, typically named on-main-push-deploy-do.yml and located in the .github/workflows directory, defines the automation logic. The workflow is triggered by a push to a specific branch, such as main. The job runs on an Ubuntu-latest runner and utilizes the appleboy/ssh-action action to execute commands on the remote server.

The workflow script performs several critical steps. First, it sets up the Node Version Manager (NVM) environment by exporting NVM_DIR and sourcing the NVM script. This ensures that the correct Node.js version is available if the application requires it. Next, it cleans up any previous deployment artifacts by removing an existing directory (e.g., test) and creating a fresh one. It then navigates into this directory and clones the GitHub repository using the SSH method. The command git clone [email protected]:username/projectname pulls the latest code. Finally, a confirmation message is echoed to indicate the successful completion of the deployment. Developers can monitor the progress of these jobs in the "Actions" tab of their GitHub repository.

yaml name: Deploy to DigitalOcean Droplet 'on': push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Deploy to DigitalOcean Droplet uses: appleboy/ssh-action@master with: host: ${{secrets.HOST}} username: ${{secrets.USERNAME}} key: ${{secrets.KEY}} script: | export NVM_DIR=~/.nvm source ~/.nvm/nvm.sh rm -rf test mkdir test cd test git clone [email protected]:username/projectname echo 'Deployment to digital ocean finished'

Deploying ShinyProxy Applications with Docker Registries

For applications requiring containerization, such as ShinyProxy deployments, the workflow becomes more complex but offers greater portability and isolation. ShinyProxy is a gateway that allows R Shiny applications to be accessed via a web browser, often deployed on DigitalOcean using a 1-click application solution from providers like Analythium. While the 1-click setup simplifies initial installation, integrating GitHub Actions for continuous deployment requires additional configuration involving Docker registries and specific user permissions.

The deployment user on the Droplet, in this case named ghactions, must have the necessary permissions to manage Docker containers. To avoid requiring sudo for every Docker command, the user is added to the docker group. This is done by logging into the Droplet and executing sudo usermod -aG docker ghactions followed by newgrp docker. This change ensures that the ghactions user can pull and run Docker images seamlessly during the automated workflow.

SSH keys are generated locally for the ghactions user and uploaded to the server to enable GitHub Actions to connect. A preliminary test workflow is recommended to verify connectivity. This test workflow triggers on pushes to a dev branch and uses the appleboy/ssh-action to create a dummy file on the remote server. If the file appears on the Droplet, the SSH connection is successful. After verification, local temporary key files should be deleted to maintain security hygiene.

A critical component of this deployment model is the Docker container registry. GitHub Actions runners need to build Docker images and push them to a registry where the Droplet can pull them from. While Docker Hub or GitHub Container Registry are options, DigitalOcean’s own container registry is often preferred for latency and integration benefits. To use this, a registry must be created for the account or team, and an access token generated. This access token is then stored as a GitHub Secret named DIGITALOCEAN_ACCESS_TOKEN.

The actual deployment workflow for a ShinyProxy application involves several sequential steps. First, the workflow checks out the dev branch. Then, it builds a Docker image using the Dockerfile in the repository. For Shiny applications, tools like golem::add_dockerfile_shinyproxy() can generate the necessary Dockerfile. The built image is then pushed to the DigitalOcean container registry with the tag latest. Finally, the workflow uses the SSH action to connect to the Droplet, pull the latest image from the registry, and restart the ShinyProxy service to reflect the changes.

Leveraging DigitalOcean App Platform for PaaS Deployments

An alternative to managing raw Droplets is using DigitalOcean’s App Platform, a fully managed Platform-as-a-Service. This approach significantly reduces infrastructure management overhead. The App Platform automatically builds, deploys, and scales components based on the source code or container images provided. GitHub Actions can be used to configure and deploy apps to this platform without interacting with the DigitalOcean control panel or API manually.

To deploy to App Platform using GitHub Actions, specific prerequisites must be met. First, the GitHub repository must contain the application’s source code. Second, a DigitalOcean API token with read and write permissions for App Platform must be stored in the repository’s secrets. This token authenticates the workflow’s deployment requests. Third, the GitHub account must be authenticated with App Platform. This is achieved by deploying an app via the control panel or API at least once, which grants App Platform the necessary permissions to access the GitHub account.

The workflow for App Platform deployment is straightforward and relies on the digitalocean/app_action/deploy@v2 action. This action handles the communication with DigitalOcean’s API, pushing the code or image to the platform. The workflow is typically triggered by a push to the main branch. The job checks out the repository code and then executes the deployment action, passing the API token from the secrets.

Three common workflow patterns exist for App Platform:
1. Direct Deployment: Deploying the app directly from the GitHub repository to App Platform.
2. Container Registry Deployment: Building a Docker image using GitHub Container Registry and deploying that image to App Platform.
3. Pull Request Testing: Deploying a unique instance of the app for each pull request, allowing developers to test changes in a live environment before merging them into the main branch.

Setting up the workflow involves navigating to the "Actions" tab in the GitHub repository and creating a new workflow file, such as deploy-app.yaml. The user pastes the workflow template, updates the secret variable name to match their stored API token, and commits the changes. This automation ensures that every push to the main branch results in an immediate, safe deployment to the App Platform.

yaml name: Update App on: push: branches: [main] permissions: contents: read jobs: deploy-app: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Deploy the app uses: digitalocean/app_action/deploy@v2 with: token: ${{ secrets.YOUR_DIGITALOCEAN_ACCESS_TOKEN_VARIABLE_NAME }}

Conclusion

Integrating GitHub Actions with DigitalOcean infrastructure represents a significant leap in deployment efficiency and reliability. For developers requiring granular control over their environment, configuring SSH access to a Droplet allows for customized setups, such as running ShinyProxy with specific Docker configurations. This method demands careful attention to user permissions, SSH key management, and registry integration. Conversely, for teams prioritizing speed and reduced maintenance, the App Platform offers a managed PaaS solution where GitHub Actions seamlessly pushes code or images via API tokens. Both approaches eliminate manual deployment steps, reduce human error, and ensure that the production environment remains synchronized with the latest code changes. The choice between a raw Droplet and the App Platform depends on the specific needs of the application, but the underlying automation principles remain consistent: secure secrets, automated workflows, and continuous integration.

Sources

  1. Daniel Torscho's GitHub Gist
  2. Deploy Shiny Application to Digital Ocean Using GitHub Actions
  3. Deploy from GitHub Actions | DigitalOcean Docs

Related Posts