<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Josh Lee</title>
    <description>The latest articles on Forem by Josh Lee (@toficofi).</description>
    <link>https://forem.com/toficofi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F707870%2F1db90336-c5bd-4cfb-8481-88c5ed62e1f1.jpg</url>
      <title>Forem: Josh Lee</title>
      <link>https://forem.com/toficofi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/toficofi"/>
    <language>en</language>
    <item>
      <title>Make your own Heroku! Auto-deploy a Docker app on your own server with GitHub Actions</title>
      <dc:creator>Josh Lee</dc:creator>
      <pubDate>Mon, 20 Sep 2021 20:44:47 +0000</pubDate>
      <link>https://forem.com/toficofi/make-your-own-heroku-auto-deploy-a-docker-app-on-your-own-server-with-github-actions-5d32</link>
      <guid>https://forem.com/toficofi/make-your-own-heroku-auto-deploy-a-docker-app-on-your-own-server-with-github-actions-5d32</guid>
      <description>&lt;p&gt;If you've switched from using PaaS services like Heroku to running off a VPS (or your own machine!) then you might miss features like auto-deploy.&lt;/p&gt;

&lt;p&gt;Luckily, with the power of ⚡ GitHub Actions and Docker, it's simple to replicate auto-deploy yourself.&lt;/p&gt;

&lt;p&gt;You can find the final example repository below:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/toficofi" rel="noopener noreferrer"&gt;
        toficofi
      &lt;/a&gt; / &lt;a href="https://github.com/toficofi/auto-deploy-docker" rel="noopener noreferrer"&gt;
        auto-deploy-docker
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  🎯 The Goal
&lt;/h2&gt;

&lt;p&gt;We'll be able to &lt;code&gt;git push origin live&lt;/code&gt; some changes, and find that your server has automatically pulled the changes, built a new image, and restarted the containers, without lifting a finger.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 What do I need?
&lt;/h2&gt;

&lt;p&gt;This guide assumes a basic understanding of Docker, Git, and bash, and that you have a VPS or another Linux machine already provisioned. The stack for your app doesn't matter here!&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Let's get started!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Our app
&lt;/h3&gt;

&lt;p&gt;In this example, we'll be using the default ASP.Net Core webapp template. It doesn't matter what stack you use, as long as it's Dockerized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;auto-deploy-docker
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;auto-deploy-docker
&lt;span class="nv"&gt;$ &lt;/span&gt;dotnet new webapp
&lt;span class="nv"&gt;$ &lt;/span&gt;dotnet run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(local shell)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That'll get us started with a simple webapp, found at &lt;code&gt;localhost:5000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0otb8ojrwcy73e2qvt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0otb8ojrwcy73e2qvt6.png" alt="Untitled-1"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🐳 Docker time
&lt;/h3&gt;

&lt;p&gt;Drop a &lt;code&gt;Dockerfile&lt;/code&gt; at the root of the project. We're going to steal the config from &lt;a href="https://docs.docker.com/samples/dotnetcore/" rel="noopener noreferrer"&gt;Dockerize an ASP.NET Core application&lt;/a&gt; as it fits our needs. I've annotated the file with comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;
&lt;span class="c"&gt;# First stage: build the app&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:5.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build-env&lt;/span&gt;

&lt;span class="c"&gt;# Make and switch to the /app directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy the C# project file into /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; *.csproj ./&lt;/span&gt;

&lt;span class="c"&gt;# Restore dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet restore

&lt;span class="c"&gt;# Now we can copy the rest of the source into /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./ ./&lt;/span&gt;

&lt;span class="c"&gt;# Build the project in Release mode to the "out" directory&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet publish &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; out

&lt;span class="c"&gt;# Second stage: run the app&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/dotnet/aspnet:5.0&lt;/span&gt;

&lt;span class="c"&gt;# Switch back to /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app &lt;/span&gt;

&lt;span class="c"&gt;# Copy the binaries from the build stage into our new stage&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-env /app/out .&lt;/span&gt;

&lt;span class="c"&gt;# Set the entrypoint for when the image is started&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "auto-deploy-docker.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To reduce the size of the image, drop a &lt;code&gt;.dockerignore&lt;/code&gt; so we don't copy in the binaries from the context. We don't need these, because we're building the project inside the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bin/
obj/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: For deployment pipelines, it's &lt;a href="https://docs.docker.com/samples/dotnetcore/#method-2-build-app-outside-docker-container" rel="noopener noreferrer"&gt;generally preferred to build the project outside the container&lt;/a&gt;, and use the compiled binaries in the image. We won't worry about that for this guide.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's build the image and verify it works locally. Make sure you exit the &lt;code&gt;dotnet run dev&lt;/code&gt; you started earlier, so it doesn't hog up port 5000!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# build the image&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; auto-deploy-docker

&lt;span class="c"&gt;# run the image, binding the container's 80 to our 5000&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:80 run auto-deploy-docker 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(local shell)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Navigate again to &lt;code&gt;localhost:5000&lt;/code&gt; and you should see the same app. &lt;/p&gt;

&lt;p&gt;Great! The last thing we'll want to do here is make use of the powerful &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt;. If you've not used Compose yet, it's basically a way to define all the containers we need to use for our app with a &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration file, which means we can put them all up and tear them all down in one go without faffing around with other commands. &lt;/p&gt;

&lt;p&gt;In this example, we're only running a single webapp in a container, but if you need to add other services - like a background worker, or a database container, Compose helps you orchestrate them. On top of that, it lets you configure containers to automatically restart when they go down or the host machine is restarted.&lt;/p&gt;

&lt;p&gt;Make a &lt;code&gt;docker-compose.yml&lt;/code&gt; and fill it out as so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;auto_deploy_docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# name of the service&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto-deploy-docker&lt;/span&gt; &lt;span class="c1"&gt;# the image to use&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto-deploy-docker&lt;/span&gt; &lt;span class="c1"&gt;# what to label the container for docker ps&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;80:80&lt;/span&gt; &lt;span class="c1"&gt;# note, we're mapping to port 80 instead of 5000 because we'll use 80 on the VPS&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt; &lt;span class="c1"&gt;# restart if failed, until we stop it ourselves&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give it a shot (again, make sure you stop the previous &lt;code&gt;docker run&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# bring everything up (will run in background)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# tear everything down&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(local shell)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Nice. We've got everything up and running locally. &lt;a href="https://github.com/git-guides/" rel="noopener noreferrer"&gt;Commit your app to GitHub&lt;/a&gt;, and make a &lt;code&gt;live&lt;/code&gt; branch that reflects what will be live on our VPS. This is where we'll push changes to have them deployed on the server.&lt;/p&gt;




&lt;h3&gt;
  
  
  💻 Dropping our app on the VPS
&lt;/h3&gt;

&lt;p&gt;It's time to take things remote. SSH into your VPS (I'm using &lt;a href="https://www.chiark.greenend.org.uk/~sgtatham/putty/" rel="noopener noreferrer"&gt;PuTTY&lt;/a&gt;), and make sure you have Git and Docker installed. I'm using a Debian with Docker image from OVH. &lt;a href="https://us.ovh.com/us/order/vps/?v=3#/vps/build?selection=~(range~'Starter~flavor~'vps-starter-1-2-20~datacenters~(BHS~1)~pricingMode~'default)" rel="noopener noreferrer"&gt;At the moment, they're doing a nice $3.50 a month VPS.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start by enabling credential caching. This will save our GitHub username and Personal Access Token. &lt;strong&gt;It will, however, save it in plaintext, so make sure you have tight control over your VPS.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; credential.helper store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, clone your repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/toficofi/auto-deploy-docker.git
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;auto-deploy-docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git will prompt you to log into GitHub if you haven't already. You can't use your GitHub password here, you'll need to &lt;a href="https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token" rel="noopener noreferrer"&gt;generate a Personal Access Token&lt;/a&gt; and use that in place of a password. &lt;em&gt;Limit the scopes for the PAT. We only need &lt;code&gt;repo&lt;/code&gt; access on the VPS.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Great! Let's give it a test run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# build the image on the server&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; auto-deploy-docker
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up
&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout live &lt;span class="c"&gt;# switch to our live branch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(remote shell)&lt;/em&gt;&lt;br&gt;
Navigate to your server's IP address, and you should see the page up and running. Fantastic!&lt;/p&gt;

&lt;p&gt;Next up, we'll write a bash script for automatically pulling changes and deploying them.&lt;/p&gt;


&lt;h3&gt;
  
  
  📝 The deploy script
&lt;/h3&gt;

&lt;p&gt;Let's recap where we are.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have an ASP.Net Core app, and we've Dockerized it.&lt;/li&gt;
&lt;li&gt;We've got it on GitHub, and the VPS has the repo pulled.&lt;/li&gt;
&lt;li&gt;We can run &lt;code&gt;docker-compose up&lt;/code&gt; to start our container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to respond to changes to the code, these are the steps we need to take:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull down the updated &lt;code&gt;live&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;Build the image (which will also build the project)&lt;/li&gt;
&lt;li&gt;Tear down the existing containers&lt;/li&gt;
&lt;li&gt;Start up the existing container&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll wrap these steps up in a little &lt;code&gt;deploy.sh&lt;/code&gt; script that will handle this all with a single execution.&lt;/p&gt;

&lt;p&gt;Make the &lt;code&gt;deploy.sh&lt;/code&gt; script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deploying changes..."&lt;/span&gt;
&lt;span class="c"&gt;# Pull changes from the live branch&lt;/span&gt;
git pull

&lt;span class="c"&gt;# Build the image with the new changes&lt;/span&gt;
docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; auto-deploy-docker

&lt;span class="c"&gt;# Shut down the existing containers&lt;/span&gt;
docker-compose down

&lt;span class="c"&gt;# Start the new containers&lt;/span&gt;
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deployed!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: If you're developing on a Windows platform, make sure you change your line endings to use &lt;code&gt;\r&lt;/code&gt; (Unix-style) instead of &lt;code&gt;\r\n&lt;/code&gt;, or bash on the server won't understand the script.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Make the script executable, and give it a shot!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x deploy.sh
&lt;span class="nv"&gt;$ &lt;/span&gt;deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(remote shell)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It might take a couple of minutes, but you'll be able to watch it pull changes, build your image, and start the containers up. &lt;/p&gt;

&lt;p&gt;Connect to your VPS and you should see the app!&lt;/p&gt;




&lt;h3&gt;
  
  
  ⚡ Setting up the GitHub Action
&lt;/h3&gt;

&lt;p&gt;We've got our &lt;code&gt;deploy.sh&lt;/code&gt; script ready to go. The final piece of the puzzle is to set up a GitHub Action that will automatically invoke this script whenever the &lt;code&gt;live&lt;/code&gt; branch has been updated, completing the loop.&lt;/p&gt;

&lt;p&gt;Jump onto your repo on GitHub, navigate to the &lt;em&gt;Actions&lt;/em&gt; page and hit &lt;em&gt;skip this and set up a workflow yourself&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo10cr2ga7eufwj4r7fcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo10cr2ga7eufwj4r7fcm.png" alt="aa"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll be presented with a templated Workflow script. It looks a little overwhelming, but it's really just a list of "actions". Each Action is triggered by something, like a push to a branch. It will then execute a sequence of steps in a GitHub-provided container. &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;You can read more about Actions here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're just going to use the action to SSH and invoke our &lt;code&gt;deploy.sh&lt;/code&gt; script whenever the &lt;code&gt;live&lt;/code&gt; branch is pushed to.&lt;/p&gt;

&lt;p&gt;Drop this workflow script in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;

&lt;span class="c1"&gt;# Controls when the workflow will run&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Triggers the workflow on push or pull request events but only for the live branch&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;live&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;live&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Allows you to run this workflow manually from the Actions tab&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="c1"&gt;# A workflow run is made up of one or more jobs that can run sequentially or in parallel&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The type of runner that the job will run on&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="c1"&gt;# Steps represent a sequence of tasks that will be executed as part of the job&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log into VPS and trigger deploy script&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.HOST }}&lt;/span&gt; &lt;span class="c1"&gt;# uses secrets stored in the Secrets tab&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PASSWORD }}&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PORT }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sh deploy.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should be pretty self explanatory. Commit the workflow script to the &lt;code&gt;live&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;We'll need to allow the Action to access our SSH host, username, password and port. Go to Settings &amp;gt; Secrets.&lt;/p&gt;

&lt;p&gt;Add secrets for &lt;code&gt;HOST&lt;/code&gt; (IP/domain for your server), &lt;code&gt;USERNAME&lt;/code&gt; (ssh username), &lt;code&gt;PASSWORD&lt;/code&gt; (ssh password) &lt;code&gt;PORT&lt;/code&gt; (probably 22).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3p5wu96r9exirddmal8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3p5wu96r9exirddmal8r.png" alt="secrets"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE: These secrets are accessible to anyone with Contributor access to the repository, which means they'll be able to log into your VPS. Be careful.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With the secrets added, that should be us ready to go. Let's give it a whirl! &lt;/p&gt;

&lt;p&gt;On your local machine, make a change to your app. For example, we'll change &lt;code&gt;Index.cshtml&lt;/code&gt; to show "Hello, Auto Deploy!"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"display-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Welcome&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello, Auto Deploy!&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push your changes to the &lt;code&gt;live&lt;/code&gt; branch, and jump onto GitHub to watch the action. You'll be able to see GitHub executing each step for you. Once it's done, navigate to your server IP/domain and some ✨magic✨ has happened!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghdhxrkqnsv51ya5l9x0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghdhxrkqnsv51ya5l9x0.png" alt="done"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎁 Wrapping up
&lt;/h2&gt;

&lt;p&gt;Now that you've completed this guide, you'll be able to deploy your changes on your VPS with a simple &lt;code&gt;git push&lt;/code&gt;. This will save you a ton of time, and makes a foundation for a CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;Here are some next steps you might take:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run some tests as part of your deployment pipeline&lt;/li&gt;
&lt;li&gt;Build the app using GitHub Actions, and send over the binaries via SCP instead of maintaining a repo on the VPS&lt;/li&gt;
&lt;li&gt;Send yourself a notification when the deployment has completed with &lt;a href="https://github.com/GustavoKatel/pushbullet-cli" rel="noopener noreferrer"&gt;PushBullet CLI&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎉 Thanks for reading! &lt;a href="https://twitter.com/toficofi" rel="noopener noreferrer"&gt;You can find me on Twitter here.&lt;/a&gt; and on &lt;a href="https://www.linkedin.com/in/josh-lee-15459b10a/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; here.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>github</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
