<?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>DEV Community: David Huertas</title>
    <description>The latest articles on DEV Community by David Huertas (@ikurotime).</description>
    <link>https://dev.to/ikurotime</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%2F733558%2Fbe49e9c1-9a6f-4f0b-8693-a4024ab542d6.png</url>
      <title>DEV Community: David Huertas</title>
      <link>https://dev.to/ikurotime</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ikurotime"/>
    <language>en</language>
    <item>
      <title>Deploy docker containers in VPS with GitHub Actions</title>
      <dc:creator>David Huertas</dc:creator>
      <pubDate>Wed, 24 Jul 2024 18:38:18 +0000</pubDate>
      <link>https://dev.to/ikurotime/deploy-docker-containers-in-vps-with-github-actions-2e28</link>
      <guid>https://dev.to/ikurotime/deploy-docker-containers-in-vps-with-github-actions-2e28</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;For the spanish version, check &lt;a href="https://davidhuertas.dev/es/posts/subir-contenedores-de-docker-en-un-vps-usando-github-actions" rel="noopener noreferrer"&gt;my personal blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We all love Docker, and if you don't, you should. &lt;/p&gt;

&lt;p&gt;Docker is a great tool that allows us to create containers with our applications and run them in any environment. But what if we want to deploy our containers in a VPS? In this post, I will show you how to deploy your Docker containers in a VPS using GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are we going to do?
&lt;/h2&gt;

&lt;p&gt;We are going to build a simple workflow that will allow us to deploy our custom images to &lt;a href="https://github.com/features/packages" rel="noopener noreferrer"&gt;GitHub Container Registry&lt;/a&gt;, pull them from our VPS and run them.&lt;/p&gt;

&lt;p&gt;Essentially, this will be just like having a simple CI/CD pipeline.&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%2Fy2hakl1jxtbcvdt6difm.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%2Fy2hakl1jxtbcvdt6difm.png" alt="Workflow diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: Workflow diagram example of the CI/CD pipeline&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start, you need to have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A VPS with Docker installed&lt;/li&gt;
&lt;li&gt;A GitHub repository with your app&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Select a GitHub repository
&lt;/h2&gt;

&lt;p&gt;You can either create a new repo or use an existing one. For this example, I will use a simple Node.js app.&lt;/p&gt;

&lt;p&gt;We will use the &lt;a href="https://hono.dev/" rel="noopener noreferrer"&gt;Hono&lt;/a&gt; framework. You can install it by running:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;hono @hono/node-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;serve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@hono/node-server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hono&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hono&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello Node.js!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server started!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;// Response from http://localhost:3000/
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; node index.js
&lt;span class="go"&gt;Hello Node.js!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is the folder structure so far:&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;.&lt;/span&gt;
├── node_modules/ 
├── index.js
├── package-lock.json
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Step 2: Create a Dockerfile
&lt;/h2&gt;

&lt;p&gt;In the root of your project, create a new file called &lt;code&gt;Dockerfile&lt;/code&gt; and add the following content:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.js .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; 

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are using an M1 Mac or similar you will need to add the &lt;code&gt;--platform linux/amd64&lt;/code&gt; flag to the Dockerfile&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; --platform linux/amd64 node:18-alpine&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file will create a new image based on the &lt;code&gt;node:18-alpine&lt;/code&gt; image, install the dependencies of your app and then run it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can build the image by running:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; vps_tutorial &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And run it with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 vps_tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If we go to &lt;code&gt;http://localhost:3000/&lt;/code&gt; we should see the message &lt;code&gt;Hello Node.js!&lt;/code&gt;. Awesome!&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%2Fcm4e5b0cidvej5tjgdhv.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%2Fcm4e5b0cidvej5tjgdhv.png" alt="Image of server running"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2: Server working as expected&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Get Secrets and SSH keys
&lt;/h2&gt;

&lt;p&gt;We need to get some secrets to make this work. I'll explain it in a bit&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GH_SECRET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSH_USER&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSH_HOST&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSH_PRIVATE_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WORK_DIR&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we need to get a GitHub API key. This will allow the GitHub Action Workflow to push our image to the  Registry.&lt;/p&gt;

&lt;p&gt;On GitHub, go to &lt;code&gt;Settings&lt;/code&gt; -&amp;gt; &lt;code&gt;Developer settings&lt;/code&gt; -&amp;gt; &lt;code&gt;Personal access tokens&lt;/code&gt; and click on &lt;code&gt;Generate new token&lt;/code&gt;. Select the &lt;code&gt;write:packages&lt;/code&gt; scope and click on &lt;code&gt;Generate token&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's a shortcut to the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/settings/tokens/new?scopes=write:packages,read:packages,delete:packages" rel="noopener noreferrer"&gt;https://github.com/settings/tokens/new?scopes=write:packages,read:packages,delete:packages&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will generate a Classic Token, this will be our &lt;code&gt;GH_SECRET&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For the next step, we need to get the SSH private key of our VPS.&lt;br&gt;
You will need to create a new user for the GitHub actions if you don't have one already. You can use whatever user you like, of course.&lt;/p&gt;

&lt;p&gt;The user will be our &lt;code&gt;SSH_USER&lt;/code&gt;.&lt;br&gt;
The IP address of your VPS will be our &lt;code&gt;SSH_HOST&lt;/code&gt;.&lt;br&gt;
The directory where you want to deploy your app will be our &lt;code&gt;WORK_DIR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Log into your VPS.&lt;br&gt;
Then, generate one by running:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Copy the content of the public key and add it to the &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; file of the user you want to use.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cat &amp;lt;path/to/public/key&amp;gt; &amp;gt;&amp;gt; ~/.ssh/authorized_keys&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, copy the content of the &lt;em&gt;private&lt;/em&gt; key to a new file.&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;cat&lt;/span&gt; &amp;lt;path/to/private/key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will be our &lt;code&gt;SSH_PRIVATE_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that the public key will be the one with the &lt;code&gt;.pub&lt;/code&gt; extension. The private key will be the one without it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have all the secrets, we can set the secrets in our GitHub repository.&lt;/p&gt;

&lt;p&gt;Go to the repo &lt;code&gt;Security&lt;/code&gt; -&amp;gt; &lt;code&gt;Secrets and Variables&lt;/code&gt; -&amp;gt; Actions and click on &lt;code&gt;New repository secret&lt;/code&gt;. Add the secrets with the names mentioned above.&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%2Fk9uxbl5653a5muylhrt1.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%2Fk9uxbl5653a5muylhrt1.png" alt="Github secrets"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Create a GitHub Actions workflow
&lt;/h2&gt;

&lt;p&gt;In your GitHub repository, create a new folder called &lt;code&gt;.github/workflows&lt;/code&gt; and add a new file called &lt;code&gt;docker-publish.yml&lt;/code&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;publish&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;main'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;REGISTRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}/&amp;lt;image-name&amp;gt;:latest&lt;/span&gt; &lt;span class="c1"&gt;# Change &amp;lt;image-name&amp;gt; to your image name&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;publish&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;publish image&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="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;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&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;login&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo ${{ secrets.GH_SECRET }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin&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;Build and Publish&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker build . --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}&lt;/span&gt;
          &lt;span class="s"&gt;docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publish&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;deploy image&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="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;install ssh keys&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;install -m 600 -D /dev/null ~/.ssh/id_rsa&lt;/span&gt;
          &lt;span class="s"&gt;echo "${{ secrets.SSH_PRIVATE_KEY }}" &amp;gt; ~/.ssh/id_rsa&lt;/span&gt;
          &lt;span class="s"&gt;ssh-keyscan -H ${{ secrets.SSH_HOST }} &amp;gt; ~/.ssh/known_hosts&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;connect and pull&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.WORK_DIR }} &amp;amp;&amp;amp; docker compose pull &amp;amp;&amp;amp; docker compose up -d &amp;amp;&amp;amp; exit"&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;cleanup&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rm -rf ~/.ssh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The workflow will trigger on every push to the &lt;code&gt;master&lt;/code&gt; branch. It will build the image, push it to the  Registry, and then log into the VPS, pull the image and run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Upload the image to GitHub Container Registry
&lt;/h2&gt;

&lt;p&gt;We are almost ready, but first, we need to generate the image and push it to the GitHub Container Registry so the workflow can pull it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Export the &lt;code&gt;GH_SECRET&lt;/code&gt; variable in your terminal:
&lt;code&gt;bash
export GH_SECRET=&amp;lt;GH_SECRET&amp;gt;
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Login to the container registry:
&lt;code&gt;bash
echo $GH_SECRET | docker login ghcr.io -u &amp;lt;username&amp;gt; --password-stdin
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Build and push the image:
&lt;code&gt;bash
docker build . -t ghcr.io/&amp;lt;username&amp;gt;/&amp;lt;image-name&amp;gt;:latest &amp;amp;&amp;amp; docker push ghcr.io/&amp;lt;username&amp;gt;/&amp;lt;image-name&amp;gt;:latest
&lt;/code&gt;
The image will be pushed to the GitHub Container Registry.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In your server, you will need to repeat the steps 1 and 2 so the server can pull the image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Run the image on your server
&lt;/h2&gt;

&lt;p&gt;Create a new file called &lt;code&gt;docker-compose.yml&lt;/code&gt; in the same route as the &lt;code&gt;WORK_DIR&lt;/code&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Add more services if needed&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;&lt;span class="pi"&gt;:&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;&amp;lt;name&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Change this to your container name&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;ghcr.io/{username}/&amp;lt;image&amp;gt;:latest&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;3000:3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After creating the file, build the container by running:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Congratulations! You have successfully deployed your Docker container to your VPS using GitHub Actions!&lt;/p&gt;

&lt;p&gt;Now every time you push to the &lt;code&gt;main&lt;/code&gt; branch, the workflow will trigger and deploy the new image to your VPS.&lt;/p&gt;

&lt;p&gt;Here is a link to the full repository if you want to check it out:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ikurotime/vps_github_actions_tutorial" rel="noopener noreferrer"&gt;https://github.com/ikurotime/vps_github_actions_tutorial&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>vps</category>
      <category>githubactions</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
