<?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: Veerendra K</title>
    <description>The latest articles on DEV Community by Veerendra K (@veerendra2).</description>
    <link>https://dev.to/veerendra2</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%2F1111511%2F2b928c95-b457-4ffd-9aba-0e308cee4ed0.png</url>
      <title>DEV Community: Veerendra K</title>
      <link>https://dev.to/veerendra2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/veerendra2"/>
    <language>en</language>
    <item>
      <title>How to Deploy rest-server(Restic) on Docker Swarm Behind Traefik Reverse Proxy</title>
      <dc:creator>Veerendra K</dc:creator>
      <pubDate>Thu, 20 Jul 2023 23:18:19 +0000</pubDate>
      <link>https://dev.to/veerendra2/how-to-deploy-rest-serverrestic-on-docker-swarm-behind-traefik-reverse-proxy-4a8h</link>
      <guid>https://dev.to/veerendra2/how-to-deploy-rest-serverrestic-on-docker-swarm-behind-traefik-reverse-proxy-4a8h</guid>
      <description>&lt;p&gt;As you might know &lt;a href="https://restic.net/" rel="noopener noreferrer"&gt;Restic&lt;/a&gt;, a simple backup manager. I have been using &lt;code&gt;restic&lt;/code&gt; for a while now, backing up all my data easily and securely. For example...&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;# Initialize a backup repository in the specified local directory:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;restic init &lt;span class="nt"&gt;--repo&lt;/span&gt; path/to/repository

&lt;span class="c"&gt;# Backup a directory to the repository:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;restic &lt;span class="nt"&gt;--repo&lt;/span&gt; path/to/repository backup path/to/directory

&lt;span class="c"&gt;# Show backup snapshots currently stored in the repository:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;restic &lt;span class="nt"&gt;--repo&lt;/span&gt; path/to/repository snapshots

&lt;span class="c"&gt;# Restore a specific backup snapshot to a target directory:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;restic &lt;span class="nt"&gt;--repo&lt;/span&gt; path/to/repository restore latest|snapshot_id &lt;span class="nt"&gt;--target&lt;/span&gt; path/to/target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When it comes to managing remote backups, &lt;code&gt;restic&lt;/code&gt; supports &lt;a href="https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#sftp" rel="noopener noreferrer"&gt;&lt;code&gt;sftp&lt;/code&gt;&lt;/a&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;restic &lt;span class="nt"&gt;-r&lt;/span&gt; sftp:user@host:/srv/restic-repo init
enter password &lt;span class="k"&gt;for &lt;/span&gt;new repository:
enter password again:
created restic repository f1c6108821 at sftp:user@host:/srv/restic-repo
Please note that knowledge of your password is required to access the repository.
Losing your password means that your data is irrecoverably lost.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is one more option to manage remote backups, that is via http with &lt;a href="https://github.com/restic/rest-server" rel="noopener noreferrer"&gt;&lt;code&gt;rest-server&lt;/code&gt;&lt;/a&gt; tool.&lt;/p&gt;

&lt;p&gt;Basically, you run &lt;code&gt;rest-server&lt;/code&gt; as a systemd daemon or in docker to expose http endpoint and &lt;code&gt;restic&lt;/code&gt; client can interact with this http to manage backups on remote locations.&lt;/p&gt;

&lt;p&gt;Then you might ask, why use this http &lt;code&gt;rest-server&lt;/code&gt;, when we have &lt;code&gt;sftp&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;As &lt;a href="https://github.com/restic/rest-server#why-use-rest-server" rel="noopener noreferrer"&gt;&lt;code&gt;rest-server&lt;/code&gt; READM.md&lt;/a&gt; says&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Compared to the SFTP backend, the REST backend has better performance.&lt;/li&gt;
&lt;li&gt;REST protocol should be faster and more scalable, due to some inefficiencies of the SFTP protocol.&lt;/li&gt;
&lt;li&gt;Rest Server adds is the optional ability to run in append-only mode.&lt;/li&gt;
&lt;li&gt;Rest Server implementation is really simple and as such could be used on the low-end devices, no problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 &lt;a href="https://github.com/veerendra2/raspberrypi-homeserver/tree/main/services/rest-server" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt; contains docker stack files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I considered this tool &lt;code&gt;rest-server&lt;/code&gt; for my home server to manage backups, that's why I deployed &lt;code&gt;rest-server&lt;/code&gt; behind Traefik reverse proxy with HTTPS. Here is the diagram below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ft3r9cr5470vsjrzr6n9h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ft3r9cr5470vsjrzr6n9h.png" alt="rest-server arch" width="647" height="277"&gt;&lt;/a&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 clone https://github.com/veerendra2/raspberrypi-homeserver.git
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;raspberrypi-homeserver/services/rest-server
&lt;span class="nv"&gt;$ &lt;/span&gt;tree &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── .env_rest-server
├── docker-stack.yml
└── secrets
    └── htpasswd.txt

1 directory, 3 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, here is my docker swarm service for reference&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;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;network_monitoring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;network_public&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;htpasswd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secrets/htpasswd.txt&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;rest-server&lt;/span&gt;&lt;span class="pi"&gt;:&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;restic/rest-server:latest&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;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;placement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;constraints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;node.role == manager&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;restart_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on-failure&lt;/span&gt;
        &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
        &lt;span class="na"&gt;max_attempts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.enable=true&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.docker.network=network_public&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.rest-server.tls=true&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.rest-server.rule=Host(`veeru.duckdns.com`) &amp;amp;&amp;amp; PathPrefix(`/restic`)&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.services.rest-server.loadbalancer.server.port=8000&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rest-server&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1000:1003&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env_rest-server&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/media/disk2/backups:/restic:rw&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;network_public&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;network_monitoring&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;htpasswd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;▶️If you want to know more about how I deployed Traefik reverse proxy, refer my &lt;a href="https://github.com/veerendra2/raspberrypi-homeserver/tree/main/services/traefik" rel="noopener noreferrer"&gt;github repo&lt;/a&gt;&lt;br&gt;
▶️If you want to know more about how I used duckdns sub-domain as my home server's hostname, refer my &lt;a href="https://veerendra2.medium.com/traefik-https-config-with-duckdns-for-local-homeserver-c55db9971683" rel="noopener noreferrer"&gt;medium blog post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and the &lt;code&gt;.env_rest-server&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="nv"&gt;DISABLE_AUTHENTICATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nv"&gt;OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;--prometheus&lt;/span&gt; &lt;span class="nt"&gt;--prometheus-no-auth&lt;/span&gt; &lt;span class="nt"&gt;--append-only&lt;/span&gt; &lt;span class="nt"&gt;--private-repos&lt;/span&gt;
&lt;span class="nv"&gt;DATA_DIRECTORY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/restic
&lt;span class="nv"&gt;PASSWORD_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/secrets/htpasswd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I prefer expose the service(&lt;code&gt;rest-server&lt;/code&gt;) with sub-path(&lt;code&gt;/restic&lt;/code&gt;), that's why you see the traefik label like below&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="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.rest-server.rule=Host(`veeru.duckdns.com`) &amp;amp;&amp;amp; PathPrefix(`/restic`)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you want to expose the service with subdomain, you can set&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="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.rest-server.rule=Host(`restic.yourdomain.com`)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, if don't have personal domain, you can check my other write-up &lt;a href="https://veerendra2.medium.com/traefik-https-config-with-duckdns-for-local-homeserver-c55db9971683" rel="noopener noreferrer"&gt;Traefik HTTPS Config with DuckDNS for Local Homeserver&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;I disabled authentication as you can see &lt;code&gt;DISABLE_AUTHENTICATION=1&lt;/code&gt;.  But still, you need to create user to interact with &lt;code&gt;rest-server&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="nv"&gt;$ &lt;/span&gt;htpasswd &lt;span class="nt"&gt;-B&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; .htpasswd restic
New password:
Re-type new password:
Adding password &lt;span class="k"&gt;for &lt;/span&gt;user restic

&lt;span class="c"&gt;# create htpasswd&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .htpasswd
restic:[REDACTED]

&lt;span class="c"&gt;# save htpasswd content in secrets directory to use it as docker secrets&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; .htpasswd &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; secrets/htpasswd.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Data directory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DATA_DIRECTORY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/restic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This option tells &lt;code&gt;rest-server&lt;/code&gt; where our restic repositories should be stored. Since we are running in Docker, we can mount this data directory outside of docker in the desired location as you can see in docker stack file.&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="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/media/disk2/backups:/restic:rw&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prometheus monitoring
&lt;/h3&gt;

&lt;p&gt;You can also enable Prometheus monitoring, by setting option &lt;code&gt;--prometheus&lt;/code&gt; and &lt;code&gt;--prometheus-no-auth&lt;/code&gt; is for no authentication for Prometheus scrap&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Options
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--append-only&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The --append-only mode allows creation of new backups but prevents deletion and modification of existing backups. This can be useful when backing up systems that have a potential of being hacked.&lt;br&gt;
and  &lt;code&gt;--private-repos&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--private-repos&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;To prevent your users from accessing each others' repositories, you may use the --private-repos flag which grants access only when a subdirectory with the same name as the user is specified in the repository URL. For example, user "foo" using the repository URLs rest:&lt;a href="https://foo:pass@host:8000/foo" rel="noopener noreferrer"&gt;https://foo:pass@host:8000/foo&lt;/a&gt; or rest:&lt;a href="https://foo:pass@host:8000/foo/" rel="noopener noreferrer"&gt;https://foo:pass@host:8000/foo/&lt;/a&gt; would be granted access, but the same user using repository URLs rest:&lt;a href="https://foo:pass@host:8000/" rel="noopener noreferrer"&gt;https://foo:pass@host:8000/&lt;/a&gt; or rest:&lt;a href="https://foo:pass@host:8000/foobar/" rel="noopener noreferrer"&gt;https://foo:pass@host:8000/foobar/&lt;/a&gt; would be denied access. Users can also create their own subrepositories, like /foo/bar/.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the reason we need to create a user. Each user gets a separate directory inside the data directory(&lt;code&gt;/restic&lt;/code&gt;) to store their backups. In above section, we created user called &lt;code&gt;restic&lt;/code&gt;, the &lt;code&gt;restic&lt;/code&gt; user can store backups on server at &lt;code&gt;/restic/restic&lt;/code&gt; location(So, the path is like &lt;code&gt;[DATA_DIRECTORY]/[USER]/&lt;/code&gt;). If you create a user "ramu", the user "ramu" can store backup at location &lt;code&gt;/restic/ramu/&lt;/code&gt; on the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📰&lt;a href="https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#rest-server" rel="noopener noreferrer"&gt;https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#rest-server&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Prepare restic repository(A restic repository is nothing but a folder containing your backup in encrypted format and its metadata).&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;restic init &lt;span class="nt"&gt;--repo&lt;/span&gt; rest:https://veeru.duckdns.org/restic/my-projects
enter password &lt;span class="k"&gt;for &lt;/span&gt;new repository:
enter password again:
created restic repository 9266e74132 at rest:https://veeru.duckdns.org/restic/my-projects/

Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;restic&lt;/code&gt; client intracts with &lt;code&gt;rest-server&lt;/code&gt; (with endpoint &lt;code&gt;https://veeru.duckdns.org/restic&lt;/code&gt;) and creates repository on remote location as you can below&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;cd&lt;/span&gt; /media/disk2/backups/
&lt;span class="c"&gt;# restic directory is created&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;restic

&lt;span class="c"&gt;# check files inside restic directory&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;tree &lt;span class="nt"&gt;-L&lt;/span&gt; 2 restic/
restic/
└── my-projects
    ├── config
    ├── data
    ├── index
    ├── keys
    ├── locks
    └── snapshots

6 directories, 1 file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that &lt;code&gt;/restic&lt;/code&gt; location is the sub-path we created for Traefik. Traefik will route traffic to &lt;code&gt;rest-server&lt;/code&gt; when it sees &lt;code&gt;/restic&lt;/code&gt; in the URL path.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run backup&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;restic &lt;span class="nt"&gt;-r&lt;/span&gt; rest:https://veeru.duckdns.org/restic/my-projects backup my-projects/
enter password &lt;span class="k"&gt;for &lt;/span&gt;repository:
repository 9266e741 opened &lt;span class="o"&gt;(&lt;/span&gt;version 2, compression level auto&lt;span class="o"&gt;)&lt;/span&gt;
created new cache &lt;span class="k"&gt;in&lt;/span&gt; /Users/veerendra.k/Library/Caches/restic
no parent snapshot found, will &lt;span class="nb"&gt;read &lt;/span&gt;all files
&lt;span class="o"&gt;[&lt;/span&gt;0:01] 2370 files 421.600 MiB, total 13206 files 1.562 GiB, 0 errors
...

Files:       105887 new,     0 changed,     0 unmodified
Dirs:        33890 new,     0 changed,     0 unmodified
Added to the repository: 3.807 GiB &lt;span class="o"&gt;(&lt;/span&gt;2.988 GiB stored&lt;span class="o"&gt;)&lt;/span&gt;

processed 105887 files, 4.461 GiB &lt;span class="k"&gt;in &lt;/span&gt;2:01
snapshot 9c7a6616 saved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it!, &lt;code&gt;restic&lt;/code&gt; will backup(with encrption) all files from source path to remote &lt;code&gt;rest-server&lt;/code&gt; location.&lt;/p&gt;

&lt;p&gt;List snapshots&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;restic &lt;span class="nt"&gt;-r&lt;/span&gt; rest:https://veeru.duckdns.org/restic/my-projects snapshots
enter password &lt;span class="k"&gt;for &lt;/span&gt;repository:
repository 9266e741 opened &lt;span class="o"&gt;(&lt;/span&gt;version 2, compression level auto&lt;span class="o"&gt;)&lt;/span&gt;
ID        Time                 Host          Tags        Paths
&lt;span class="nt"&gt;-----------------------------------------------------------------------------------------------&lt;/span&gt;
9c7a6616  2023-07-18 23:22:11  C02F66HKML85              /Users/veerendra.k/my-projects
&lt;span class="nt"&gt;-----------------------------------------------------------------------------------------------&lt;/span&gt;
1 snapshots
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple &lt;code&gt;rest-server&lt;/code&gt; allows me to manage my backups on remote locations.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Deploy Hugo Static Website with Github Actions</title>
      <dc:creator>Veerendra K</dc:creator>
      <pubDate>Thu, 06 Jul 2023 15:19:41 +0000</pubDate>
      <link>https://dev.to/veerendra2/hugo-website-cicd-with-github-actions-59c4</link>
      <guid>https://dev.to/veerendra2/hugo-website-cicd-with-github-actions-59c4</guid>
      <description>&lt;p&gt;As you know &lt;a href="https://gohugo.io/" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt; is a lightning-fast static website generator perfect for building modern websites. It has been almost 2 years since I’m using Hugo for my GitHub Pages blog &lt;a href="https://veerendra2.github.io/" rel="noopener noreferrer"&gt;https://veerendra2.github.io/&lt;/a&gt;. I’m comfortable using Hugo after I moved from Jekyll; &lt;a href="https://veerendra2.github.io/moving-to-hugo/" rel="noopener noreferrer"&gt;https://veerendra2.github.io/moving-to-hugo/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this write-up, I will show you how I‘m using Github Actions to build and deploy static websites on GitHub Pages.&lt;/p&gt;

&lt;p&gt;👉 Here is my GitHub Actions workflow &lt;a href="https://gist.github.com/veerendra2/28d35b44c8340d5a801e5606edb32f45" rel="noopener noreferrer"&gt;https://gist.github.com/veerendra2/28d35b44c8340d5a801e5606edb32f45&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;I have 2 GitHub repositories, one is the source and the other is the destination.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fzvvkpnvn00027yzporrm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fzvvkpnvn00027yzporrm.png" alt="Image description" width="698" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source repo contains markdown files where I write my blog content in these files and the destination contains a static HTML website which is served by GitHub Pages.&lt;/p&gt;

&lt;p&gt;You can also use single repo with 2 branches to keep markdown files and a static HTML website. In fact, my previous setup was like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fibprm5hlphj2hq40vcev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fibprm5hlphj2hq40vcev.png" alt="Image description" width="321" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basically, you keep all your source markdown files in a feature branch(In my case, it is “source”) and by using GitHub Actions, move the static HTML website to the “master” branch. You can take reference to this approach in my previous &lt;a href="https://veerendra2.github.io/ci-cd-github-pages-with-github-actions/" rel="noopener noreferrer"&gt;write-up&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like I said before, currently, I’m using 2 repos approach to keep my draft post private &lt;a href="https://github.com/veerendra2/veerendra2.github.io/issues/12" rel="noopener noreferrer"&gt;#12&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Workflow
&lt;/h1&gt;

&lt;p&gt;Usually, when I get new idea, I will create a GitHub issue for reference like below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fhko3e47e44mfpp9v0wlr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fhko3e47e44mfpp9v0wlr.png" alt="Image description" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then I do&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F71a5pbj39qp34tulng21.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F71a5pbj39qp34tulng21.png" alt="Image description" width="586" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the end, GitHub Actions trigger and publish my new blog post on &lt;a href="https://veerendra2.github.io/" rel="noopener noreferrer"&gt;https://veerendra2.github.io/&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Github Action Workflow
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 &lt;a href="https://gist.github.com/veerendra2/28d35b44c8340d5a801e5606edb32f45" rel="noopener noreferrer"&gt;https://gist.github.com/veerendra2/28d35b44c8340d5a801e5606edb32f45&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The workflow is straightforward and improved recently. The workflow triggers when the PR closed(Or you can also when the feature branch is merged to “main”).&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="nn"&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;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;closed&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Checkout both source and destination repos with “path” argument as you can see below.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&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;Checkout ${{ github.repository_owner }}/${{ env.SRC_REPO }}&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="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.SRC_REPO }}&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;Checkout ${{ github.repository_owner }}/${{ env.DEST_REPO }}&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="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
          &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.repository_owner }}/${{ env.DEST_REPO }}&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.DEST_REPO }}&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TOKEN_TO_PUSH_REPO }}&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Setup and build the Hugo website using options “-d” destination directory and “-s” source directory.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&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;Setup Hugo&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;peaceiris/actions-hugo@v2&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;hugo-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;0.110.0"&lt;/span&gt;
          &lt;span class="na"&gt;extended&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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 Hugo site&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;hugo --minify \&lt;/span&gt;
              &lt;span class="s"&gt;-s ${{ github.workspace }}/${{ env.SRC_REPO }} \&lt;/span&gt;
              &lt;span class="s"&gt;-d ${{ github.workspace }}/${{ env.DEST_REPO }}&lt;/span&gt;
&lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Finally, commit your changes and push them to the destination repo.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&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;Commit ${{ github.repository_owner }}/${{ env.DEST_REPO }}&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;cd ${{ github.workspace }}/${{ env.DEST_REPO }}&lt;/span&gt;
          &lt;span class="s"&gt;git config --global --add safe.directory ${{ github.workspace }}&lt;/span&gt;
          &lt;span class="s"&gt;git config --local user.email "actions@github.com"&lt;/span&gt;
          &lt;span class="s"&gt;git config --local user.name "GitHub actions"&lt;/span&gt;
          &lt;span class="s"&gt;git add -A&lt;/span&gt;
          &lt;span class="s"&gt;git commit -m "New changes" -a&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;Push changes&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;ad-m/github-push-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;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TOKEN_TO_PUSH_REPO }}&lt;/span&gt;
          &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workspace }}/${{ env.DEST_REPO }}&lt;/span&gt;
          &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.repository_owner }}/${{ env.DEST_REPO }}&lt;/span&gt;
          &lt;span class="na"&gt;force&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it, this simple workflow build and publishes my blog posts.&lt;/p&gt;

&lt;p&gt;But wait, there is one more trigger I placed as you might have noticed…&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="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;blog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Blog"&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yes"&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because, sometimes I make changes/fixes on the “main” branch itself, for this, it won’t trigger the pipeline to publish my changes/fixes. That’s why I have added a manually trigger like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F5njrv3u7ttvqsv4tyxdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F5njrv3u7ttvqsv4tyxdc.png" alt="Image description" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me know how you guys deploy your static website.&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>githubactions</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
