<?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: Ruan Bekker</title>
    <description>The latest articles on DEV Community by Ruan Bekker (@ruanbekker).</description>
    <link>https://dev.to/ruanbekker</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%2F292752%2F076e3ae6-5ada-48c8-9337-28cc56b40778.jpg</url>
      <title>DEV Community: Ruan Bekker</title>
      <link>https://dev.to/ruanbekker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ruanbekker"/>
    <language>en</language>
    <item>
      <title>ArgoCD Getting Started - Hands On</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Sun, 05 May 2024 07:28:24 +0000</pubDate>
      <link>https://dev.to/ruanbekker/argocd-getting-started-hands-on-1ljb</link>
      <guid>https://dev.to/ruanbekker/argocd-getting-started-hands-on-1ljb</guid>
      <description>&lt;p&gt;In this tutorial I will show you how to get started with ArgoCD on Kubernetes and we will cover the following topics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How to provision a local Kubernetes cluster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to deploy ArgoCD on Kubernetes using Helm.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to grant ArgoCD access to you Private Github Repositories.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to configure your application sets on Github, and how to deploy applications to your cluster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to get started with RBAC to create a local user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to setup SSO with Authentik.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to use Argo CD Notifications using Email.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pre-Requisites
&lt;/h2&gt;

&lt;p&gt;To follow along in this tutorial you will need the following&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://ruanbekker.hashnode.dev/create-kubernetes-clusters-with-kind" rel="noopener noreferrer"&gt;Kind&lt;/a&gt; installed (if you are provisioning Kubernetes with it)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://helm.sh/docs/intro/install/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/tasks/tools/" rel="noopener noreferrer"&gt;Kubectl&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install a Kubernetes Cluster
&lt;/h2&gt;

&lt;p&gt;If you already have a Kubernetes Cluster, you can skip this step.&lt;/p&gt;

&lt;p&gt;Define the &lt;code&gt;kind-config.yaml&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;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cluster&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kind.x-k8s.io/v1alpha4&lt;/span&gt;
&lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;control-plane&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;kindest/node:v1.26.6@sha256:6e2d8b28a5b601defe327b98bd1c2d1930b49e5d8c512e1895099e4504007adb&lt;/span&gt;
  &lt;span class="na"&gt;extraPortMappings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;hostPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;listenAddress&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.0.0.0"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
    &lt;span class="na"&gt;hostPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then create the cluster with &lt;code&gt;kind&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kind create cluster &lt;span class="nt"&gt;--name&lt;/span&gt; example &lt;span class="nt"&gt;--config&lt;/span&gt; kind-config.yaml


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  ArgoCD Installation
&lt;/h2&gt;

&lt;p&gt;You can deploy Argo CD using the kubernetes manifests and deploy them with kubectl &lt;strong&gt;or&lt;/strong&gt; you can deploy them with helm.&lt;/p&gt;

&lt;p&gt;I will be deploying Argo CD using &lt;strong&gt;Helm&lt;/strong&gt;, the reason for that is, I would eventually like to manage my argo deployment using Argo CD, and I have found when deploying it initially using manifests, it was not as smooth as compared to helm.&lt;/p&gt;

&lt;p&gt;So to start deploying Argo CD with Helm, so first we will need to add the helm chart repository where the chart is hosted:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

helm repo add argo https://argoproj.github.io/argo-helm


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then we can find the latest version using the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

helm search repo argo/argo-cd
&lt;span class="c"&gt;# NAME          CHART VERSION   APP VERSION DESCRIPTION&lt;/span&gt;
&lt;span class="c"&gt;# argo/argo-cd  6.0.13          v2.10.0     A Helm chart for Argo CD&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now since we have the version, we can get the default values and redirect the output to a file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

helm show values argo/argo-cd &lt;span class="nt"&gt;--version&lt;/span&gt; 6.0.13 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; values.yaml


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I only have one config parameter that I want to change and the rest I want to keep at defaults, so I am only defining this as my &lt;code&gt;values.yaml&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server.insecure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we can deploy argo cd to our cluster:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; argocd argo/argo-cd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; 6.0.13 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--values&lt;/span&gt; values.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; argocd &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can monitor our installation and ensure that all the pods are running in the &lt;code&gt;argocd&lt;/code&gt; namespace:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; argocd


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once the pods are running, we can retrieve the argo cd admin password from a kubernetes secret:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl get secret argocd-initial-admin-secret &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{.data.password}"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that we have the secret we can create a port-forward session so that we can access the argo cd frontend:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; argocd port-forward svc/argocd-server 8080:80


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Access the UI on &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1708292240242%2F1dd1b841-7d35-4677-b4bc-c5091cc69193.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1708292240242%2F1dd1b841-7d35-4677-b4bc-c5091cc69193.png" alt="argocd-tuturial"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we login we should see this:&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1708292506516%2F3812b061-6360-402f-aed8-0376939b82bd.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1708292506516%2F3812b061-6360-402f-aed8-0376939b82bd.png" alt="argocd-tuturial"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will see a blank canvas, we do see an option to create an application via the user interface, but we are not going to use this as we will define all our resources in a declarative manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continue Reading
&lt;/h2&gt;

&lt;p&gt;To access the rest of this post, feel free to see the updated version on my blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ruan.dev/blog/2024/26/03/getting-started-with-argo-cd-on-kubernetes---the-ultimate-guide" rel="noopener noreferrer"&gt;https://ruan.dev/blog/2024/26/03/getting-started-with-argo-cd-on-kubernetes---the-ultimate-guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>argocd</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to use the MySQL Terraform Provider</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Mon, 17 Jul 2023 13:00:00 +0000</pubDate>
      <link>https://dev.to/ruanbekker/how-to-use-the-mysql-terraform-provider-1p95</link>
      <guid>https://dev.to/ruanbekker/how-to-use-the-mysql-terraform-provider-1p95</guid>
      <description>&lt;p&gt;In this tutorial we will provision a MySQL Server with Docker and then use Terraform to provision MySQL Users, Database Schemas and MySQL Grants with the MySQL Terraform Provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  About
&lt;/h2&gt;

&lt;p&gt;Terraform is super powerful and can do a lot of things. And it shines when it provisions Infrastructure. So in a scenario where we use Terraform to provision RDS MySQL Database Instances, we might still want to provision extra MySQL Users, or Database Schemas and the respective MySQL Grants.&lt;/p&gt;

&lt;p&gt;Usually you will logon to the database and create them manually with sql syntax. But in this tutorial we want to make use of Docker to provision our MySQL Server and we would like to make use of Terraform to provision the MySQL Database Schemas, Grants and Users.&lt;/p&gt;

&lt;p&gt;Instead of using AWS RDS, I will be provisioning a MySQL Server on Docker so that we can keep the costs free, for those who are following along.&lt;/p&gt;

&lt;p&gt;We will also go through the steps on how to rotate the database password that we will be provisioning for our user.&lt;/p&gt;

&lt;h2&gt;
  
  
  MySQL Server
&lt;/h2&gt;

&lt;p&gt;First we will provision a MySQL Server on Docker Containers, I have a &lt;code&gt;docker-compose.yaml&lt;/code&gt; which is available in my &lt;a href="https://github.com/ruanbekker/quick-starts/blob/main/docker/mysql/docker-compose.yaml"&gt;quick-starts&lt;/a&gt; github repository:&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;mysql&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;mysql:8.0&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;mysql&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;3306:3306&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_DATABASE=sample&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_PASSWORD=rootpassword&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have saved that in your current working directory, you can start the container with docker compose:&lt;br&gt;
&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;You can test the mysql container by logging onto the mysql server with the correct auth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mysql mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-prootpassword&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'show databases;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should be more or less the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Database&lt;/span&gt;           &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;              &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;performance_schema&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;             &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;                &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------------------+&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Terraform
&lt;/h2&gt;

&lt;p&gt;If you don't have Terraform installed, you can install it from their &lt;a href="https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli"&gt;documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want the source code of this example, its available in my &lt;a href="https://github.com/ruanbekker/quick-starts/tree/main/terraform/mysql/petoju-provider"&gt;terraform-mysql/petoju-provider&lt;/a&gt; repository. Which you can clone and jump into the &lt;code&gt;terraform/mysql/petoju-provider&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;First we will define the &lt;code&gt;providers.tf&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;terraform &lt;span class="o"&gt;{&lt;/span&gt;
  required_providers &lt;span class="o"&gt;{&lt;/span&gt;
    mysql &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"petoju/mysql"&lt;/span&gt;
      version &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"3.0.37"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

provider &lt;span class="s2"&gt;"mysql"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;alias&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local"&lt;/span&gt;
  endpoint &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1:3306"&lt;/span&gt;
  username &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"root"&lt;/span&gt;
  password &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rootpassword"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the &lt;code&gt;main.tf&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;resource &lt;span class="s2"&gt;"random_password"&lt;/span&gt; &lt;span class="s2"&gt;"user_password"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  length           &lt;span class="o"&gt;=&lt;/span&gt; 24
  special          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true
  &lt;/span&gt;min_special      &lt;span class="o"&gt;=&lt;/span&gt; 2
  override_special &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"!#&lt;/span&gt;&lt;span class="nv"&gt;$%&lt;/span&gt;&lt;span class="s2"&gt;^&amp;amp;*()-_=+[]{}&amp;lt;&amp;gt;:?"&lt;/span&gt;
  keepers &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    password_version &lt;span class="o"&gt;=&lt;/span&gt; var.password_version
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

resource &lt;span class="s2"&gt;"mysql_database"&lt;/span&gt; &lt;span class="s2"&gt;"user_db"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  provider &lt;span class="o"&gt;=&lt;/span&gt; mysql.local
  name &lt;span class="o"&gt;=&lt;/span&gt; var.database_name
&lt;span class="o"&gt;}&lt;/span&gt;

resource &lt;span class="s2"&gt;"mysql_user"&lt;/span&gt; &lt;span class="s2"&gt;"user_id"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  provider &lt;span class="o"&gt;=&lt;/span&gt; mysql.local
  user &lt;span class="o"&gt;=&lt;/span&gt; var.database_username
  plaintext_password &lt;span class="o"&gt;=&lt;/span&gt; random_password.user_password.result
  host &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"%"&lt;/span&gt;
  tls_option &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"NONE"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

resource &lt;span class="s2"&gt;"mysql_grant"&lt;/span&gt; &lt;span class="s2"&gt;"user_id"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  provider &lt;span class="o"&gt;=&lt;/span&gt; mysql.local
  user &lt;span class="o"&gt;=&lt;/span&gt; var.database_username
  host &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"%"&lt;/span&gt;
  database &lt;span class="o"&gt;=&lt;/span&gt; var.database_name
  privileges &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SELECT"&lt;/span&gt;, &lt;span class="s2"&gt;"UPDATE"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  depends_on &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    mysql_user.user_id
  &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the &lt;code&gt;variables.tf&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;variable &lt;span class="s2"&gt;"database_name"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The name of the database that you want created."&lt;/span&gt;
  &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; string
  default     &lt;span class="o"&gt;=&lt;/span&gt; null
&lt;span class="o"&gt;}&lt;/span&gt;

variable &lt;span class="s2"&gt;"database_username"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The name of the database username that you want created."&lt;/span&gt;
  &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; string
  default     &lt;span class="o"&gt;=&lt;/span&gt; null
&lt;span class="o"&gt;}&lt;/span&gt;

variable &lt;span class="s2"&gt;"password_version"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The password rotates when this value gets updated."&lt;/span&gt;
  &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; number
  default     &lt;span class="o"&gt;=&lt;/span&gt; 0
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then our &lt;code&gt;outputs.tf&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;output &lt;span class="s2"&gt;"user"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  value &lt;span class="o"&gt;=&lt;/span&gt; mysql_user.user_id.user
&lt;span class="o"&gt;}&lt;/span&gt;

output &lt;span class="s2"&gt;"password"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  sensitive &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true
  &lt;/span&gt;value &lt;span class="o"&gt;=&lt;/span&gt; random_password.user_password.result
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;terraform.tfvars&lt;/code&gt; that defines the values of our variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;database_name     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"foobar"&lt;/span&gt;
database_username &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ruanb"&lt;/span&gt;
password_version  &lt;span class="o"&gt;=&lt;/span&gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are ready to run our terraform code, which will ultimately create a database, user and grants. Outputs the encrypted string of your password which was encrypted with your &lt;code&gt;keybase_username&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Initialise Terraform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the plan to see what terraform wants to provision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can see the following resources will be created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  &lt;span class="c"&gt;# mysql_database.user_db will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"mysql_database"&lt;/span&gt; &lt;span class="s2"&gt;"user_db"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + default_character_set &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"utf8mb4"&lt;/span&gt;
      + default_collation     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"utf8mb4_general_ci"&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"foobar"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# mysql_grant.user_id will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"mysql_grant"&lt;/span&gt; &lt;span class="s2"&gt;"user_id"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + database   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"foobar"&lt;/span&gt;
      + grant      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + host       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"%"&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + privileges &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
          + &lt;span class="s2"&gt;"SELECT"&lt;/span&gt;,
          + &lt;span class="s2"&gt;"UPDATE"&lt;/span&gt;,
        &lt;span class="o"&gt;]&lt;/span&gt;
      + table      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;
      + tls_option &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"NONE"&lt;/span&gt;
      + user       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ruanb"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# mysql_user.user_id will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"mysql_user"&lt;/span&gt; &lt;span class="s2"&gt;"user_id"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + host               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"%"&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + plaintext_password &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;sensitive value&lt;span class="o"&gt;)&lt;/span&gt;
      + tls_option         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"NONE"&lt;/span&gt;
      + user               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ruanb"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# random_password.user_password will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"random_password"&lt;/span&gt; &lt;span class="s2"&gt;"user_password"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + bcrypt_hash      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;sensitive value&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + keepers          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          + &lt;span class="s2"&gt;"password_version"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      + length           &lt;span class="o"&gt;=&lt;/span&gt; 24
      + lower            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + min_lower        &lt;span class="o"&gt;=&lt;/span&gt; 0
      + min_numeric      &lt;span class="o"&gt;=&lt;/span&gt; 0
      + min_special      &lt;span class="o"&gt;=&lt;/span&gt; 2
      + min_upper        &lt;span class="o"&gt;=&lt;/span&gt; 0
      + number           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + numeric          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + override_special &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"!#&lt;/span&gt;&lt;span class="nv"&gt;$%&lt;/span&gt;&lt;span class="s2"&gt;^&amp;amp;*()-_=+[]{}&amp;lt;&amp;gt;:?"&lt;/span&gt;
      + result           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;sensitive value&lt;span class="o"&gt;)&lt;/span&gt;
      + special          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + upper            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + password &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;sensitive value&lt;span class="o"&gt;)&lt;/span&gt;
  + user     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ruanb"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the apply which will create the database, the user, sets the password and applies the grants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then our returned output should show something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

password &lt;span class="o"&gt;=&lt;/span&gt; &amp;lt;sensitive&amp;gt;
user &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ruanb"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As our password is set as sensitive, we can access the value with &lt;code&gt;terraform output -raw password&lt;/code&gt;, let's assign the password to a variable:&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;DBPASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; password&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can exec into the mysql container and logon to the mysql server with our new credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mysql mysql &lt;span class="nt"&gt;-u&lt;/span&gt; ruanb &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$DBPASS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can see we are logged onto the mysql server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Welcome to the MySQL monitor.  Commands end with &lt;span class="p"&gt;;&lt;/span&gt; or &lt;span class="se"&gt;\g&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Your MySQL connection &lt;span class="nb"&gt;id &lt;/span&gt;is 14
Server version: 8.0.33 MySQL Community Server - GPL

mysql&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run &lt;code&gt;show databases;&lt;/code&gt; we should see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;show&lt;/span&gt; &lt;span class="n"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Database&lt;/span&gt;           &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;foobar&lt;/span&gt;             &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;performance_schema&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------------------+&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt; &lt;span class="n"&gt;sec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we want to rotate the mysql password for the user, we can update the &lt;code&gt;password_version&lt;/code&gt; variable either in our &lt;code&gt;terraform.tfvars&lt;/code&gt; or via the cli. Let's pass the variable in the cli and do a &lt;code&gt;terraform plan&lt;/code&gt; to verify the changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="nv"&gt;password_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And due to our value for the random resource keepers parameter being updated, it will trigger the value of our password to be changed, and that will let terraform update our mysql user's password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-place&lt;/span&gt;
-/+ destroy and &lt;span class="k"&gt;then &lt;/span&gt;create replacement

Terraform will perform the following actions:

  &lt;span class="c"&gt;# mysql_user.user_id will be updated in-place&lt;/span&gt;
  ~ resource &lt;span class="s2"&gt;"mysql_user"&lt;/span&gt; &lt;span class="s2"&gt;"user_id"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;id&lt;/span&gt;                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ruanb@%"&lt;/span&gt;
      ~ plaintext_password &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;sensitive value&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# (5 unchanged attributes hidden)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# random_password.user_password must be replaced&lt;/span&gt;
-/+ resource &lt;span class="s2"&gt;"random_password"&lt;/span&gt; &lt;span class="s2"&gt;"user_password"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      ~ bcrypt_hash      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;sensitive value&lt;span class="o"&gt;)&lt;/span&gt;
      ~ &lt;span class="nb"&gt;id&lt;/span&gt;               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"none"&lt;/span&gt; -&amp;gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      ~ keepers          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c"&gt;# forces replacement&lt;/span&gt;
          ~ &lt;span class="s2"&gt;"password_version"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt; -&amp;gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      ~ result           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;sensitive value&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# (11 unchanged attributes hidden)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

Plan: 1 to add, 1 to change, 1 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go ahead by updating our password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="nv"&gt;password_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To validate that the password has changed, we can try to logon to mysql by using the password variable that was created initially:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mysql mysql &lt;span class="nt"&gt;-u&lt;/span&gt; ruanb &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$DBPASS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And as you can see authentication failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql: &lt;span class="o"&gt;[&lt;/span&gt;Warning] Using a password on the &lt;span class="nb"&gt;command &lt;/span&gt;line interface can be insecure.
ERROR 1045 &lt;span class="o"&gt;(&lt;/span&gt;28000&lt;span class="o"&gt;)&lt;/span&gt;: Access denied &lt;span class="k"&gt;for &lt;/span&gt;user &lt;span class="s1"&gt;'ruanb'&lt;/span&gt;@&lt;span class="s1"&gt;'localhost'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;using password: YES&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the new password to the variable again:&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;DBPASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; password&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then try to logon again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mysql mysql &lt;span class="nt"&gt;-u&lt;/span&gt; ruanb &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$DBPASS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can see we are logged on again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Welcome to the MySQL monitor.  Commands end with &lt;span class="p"&gt;;&lt;/span&gt; or &lt;span class="se"&gt;\g&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Your MySQL connection &lt;span class="nb"&gt;id &lt;/span&gt;is 22
Server version: 8.0.33 MySQL Community Server - GPL

mysql&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;The terraform mysql provider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/petoju/mysql/latest/docs"&gt;https://registry.terraform.io/providers/petoju/mysql/latest/docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The quick-starts repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ruanbekker/quick-starts"&gt;https://github.com/ruanbekker/quick-starts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;Thanks for reading, feel free to check out my &lt;a href="https://ruan.dev/"&gt;website&lt;/a&gt;, feel free to subscribe to my &lt;a href="http://digests.ruanbekker.com/?via=ruanbekker-blog"&gt;newsletter&lt;/a&gt; or follow me at &lt;a href="https://twitter.com/ruanbekker"&gt;@ruanbekker&lt;/a&gt; on Twitter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linktree: &lt;a href="https://go.ruan.dev/links"&gt;https://go.ruan.dev/links&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Patreon: &lt;a href="https://go.ruan.dev/patreon"&gt;https://go.ruan.dev/patreon&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>mysql</category>
    </item>
    <item>
      <title>How to use the AWS Terraform Provider</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Sun, 16 Jul 2023 00:27:33 +0000</pubDate>
      <link>https://dev.to/ruanbekker/how-to-use-the-aws-terraform-provider-np4</link>
      <guid>https://dev.to/ruanbekker/how-to-use-the-aws-terraform-provider-np4</guid>
      <description>&lt;p&gt;In this post we will be using the AWS Terraform provider, from how to install Terraform, create a AWS IAM User, configure the AWS Provider and deploy a EC2 instance using Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS IAM User
&lt;/h2&gt;

&lt;p&gt;In order to authenticate against AWS’s APIs, we need to create a AWS IAM User and create Access Keys for Terraform to use to authenticate.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://aws.amazon.com/"&gt;https://aws.amazon.com/&lt;/a&gt; logon to your account, then search for IAM:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hXtjEg4G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/c53d15d3-1af2-4e15-aafb-229cc4274bf5" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hXtjEg4G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/c53d15d3-1af2-4e15-aafb-229cc4274bf5" alt="aws-iam-search-result" width="800" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select IAM, then select “Users” on the left hand side and select “Create User”, then provide the username for your AWS IAM User:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wis3iQ98--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/35f74a94-98d3-44c4-9651-a504780e5a6e" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wis3iQ98--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/35f74a94-98d3-44c4-9651-a504780e5a6e" alt="aws-iam-user-creation-wizard" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to assign permissions to our new AWS IAM User. For this scenario I will be assigning a IAM Policy directly to the user and I will be selecting the “AdministratorAccess” policy. Keep in mind that this allows admin access to your whole AWS account:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BzrX_9Jw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/779f2dac-0da9-4751-8e89-2ff33c088ae8" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BzrX_9Jw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/779f2dac-0da9-4751-8e89-2ff33c088ae8" alt="permissions-for-your-aws-iam-user" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you select the policy, select “Next” and select “Create User”. Once the user has been created, select “Users” on the left hand side, search for your user that we created, in my case “medium-terraform”.&lt;/p&gt;

&lt;p&gt;Select the user and click on “Security credentials”. If you scroll down to the “Access keys” section, you will notice we don’t have any access keys for this user:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wYi6mnGa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/45b5eb1f-999f-4e81-b439-9e8cd90f83a3" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wYi6mnGa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/45b5eb1f-999f-4e81-b439-9e8cd90f83a3" alt="aws-iam-access-keys" width="800" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to allow Terraform access to our AWS Account, we need to create access keys that Terraform will use, and because we assigned full admin access to the user, Terraform will be able to manage resources in our AWS Account.&lt;/p&gt;

&lt;p&gt;Click “Create access key”, then select the “CLI” option and select the confirmation at the bottom:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OtRS-6zv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/22b13879-6256-4de8-ab5f-aecece3be432" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OtRS-6zv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/22b13879-6256-4de8-ab5f-aecece3be432" alt="aws-iam-access-keys-wizard" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select “Next” and then select “Create access key”. I am providing a screenshot of the Access Key and Secret Access Key that has been provided, but by the time this post has been published, the key will be deleted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YMhpHgl3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/a7a4124f-dc9c-4700-b0ed-d21d7df4fa6c" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YMhpHgl3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/a7a4124f-dc9c-4700-b0ed-d21d7df4fa6c" alt="retrieve-aws-iam-access-keys" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Store your Access Key and Secret Access Key in a secure place and treat this like your passwords. If someone gets access to these keys they can manage your whole AWS Account.&lt;/p&gt;

&lt;p&gt;I will be using the &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"&gt;AWS CLI&lt;/a&gt; to configure my Access Key and Secret Access Key, as I will configure Terraform later to read my Access Keys from the Credential Provider config.&lt;/p&gt;

&lt;p&gt;First we need to configure the AWS CLI by passing the profile name, which I have chosen &lt;code&gt;medium&lt;/code&gt; for this demonstration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--profile&lt;/span&gt; medium configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will be asked to provide the access key, secret access key, aws region and the default output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;AWS Access Key ID &lt;span class="o"&gt;[&lt;/span&gt;None]: AKIATPRT2G4SGXLAC3HJ
AWS Secret Access Key &lt;span class="o"&gt;[&lt;/span&gt;None]: KODnR[............]nYTYbd
Default region name &lt;span class="o"&gt;[&lt;/span&gt;None]: eu-west-1
Default output format &lt;span class="o"&gt;[&lt;/span&gt;None]: json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify if everything works as expected we can use the following command to verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--profile&lt;/span&gt; medium sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response should look something similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"UserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AIDATPRT2G4SOAO5Y7S5Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"000000000000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::000000000000:user/medium-terraform"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Terraform
&lt;/h2&gt;

&lt;p&gt;Now that we have our AWS IAM User configured, we can install Terraform, if you don’t have Terraform installed yet, you can follow their &lt;a href="https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli"&gt;Installation Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have Terraform installed, we can setup our workspace where we will ultimately deploy a EC2 instance, but before we get there we need to create our project directory and change to that directory:&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;mkdir&lt;/span&gt; ~/terraform-demo
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/terraform-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we will create 4 files with &lt;code&gt;.tf&lt;/code&gt; extensions:&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;touch &lt;/span&gt;main.tf
&lt;span class="nb"&gt;touch &lt;/span&gt;outputs.tf
&lt;span class="nb"&gt;touch &lt;/span&gt;providers.tf
&lt;span class="nb"&gt;touch &lt;/span&gt;variables.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will define our Terraform definitions on how we want our desired infrastructure to look like. We will get to the content in the files soon.&lt;/p&gt;

&lt;p&gt;I personally love Terraform’s documentation as they are rich in examples and really easy to use.&lt;/p&gt;

&lt;p&gt;Head over to the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs"&gt;Terraform AWS Provider&lt;/a&gt; documentation and you scroll a bit down, you can see the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration"&gt;Authentication and Configuration&lt;/a&gt; section where they outline the order in how Terraform will look for credentials and we will be making use of the shared credentials file as that is where our access key and secret access key is stored.&lt;/p&gt;

&lt;p&gt;If you look at the top right corner of the Terraform AWS Provider documentation, they show you how to use the AWS Provider:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fuxYTkqL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/03a55c32-bb1c-4f48-a441-c09918c824db" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fuxYTkqL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/03a55c32-bb1c-4f48-a441-c09918c824db" alt="terraform-aws-provider-docs" width="800" height="1072"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can copy that code snippet and paste it into our &lt;code&gt;providers.tf&lt;/code&gt; file and configure the aws provider section with the &lt;code&gt;medium&lt;/code&gt; profile that we’ve created earlier.&lt;/p&gt;

&lt;p&gt;This will tell Terraform where to look for credentials in order to authenticate with AWS.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;providers.tf&lt;/code&gt; with your editor of choice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="o"&gt;{&lt;/span&gt;
  required_providers &lt;span class="o"&gt;{&lt;/span&gt;
    aws &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      version &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5.8.0"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

provider &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  shared_credentials_files &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"~/.aws/credentials"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  profile                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"medium"&lt;/span&gt;
  region                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can open &lt;code&gt;main.tf&lt;/code&gt; and populate the following to define the EC2 instance that we want to provision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;data &lt;span class="s2"&gt;"aws_ami"&lt;/span&gt; &lt;span class="s2"&gt;"latest_ubuntu"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  most_recent &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true
  &lt;/span&gt;owners &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"099720109477"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

  filter &lt;span class="o"&gt;{&lt;/span&gt;
    name   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;
    values &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-*-server-*"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  filter &lt;span class="o"&gt;{&lt;/span&gt;
    name   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"architecture"&lt;/span&gt;
    values &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"x86_64"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

resource &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"ec2"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  ami           &lt;span class="o"&gt;=&lt;/span&gt; data.aws_ami.latest_ubuntu.id
  instance_type &lt;span class="o"&gt;=&lt;/span&gt; var.instance_type
  tags &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    Name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.instance_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-ec2-instance"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example we are filtering for the latest Ubuntu 22.04 64bit AMI then we are defining a EC2 instance and specifying the AMI ID that we filtered from our data source.&lt;/p&gt;

&lt;p&gt;Note that we haven’t specified a SSH Keypair, as we are just focusing on how to provision a EC2 instance.&lt;/p&gt;

&lt;p&gt;As you can see we are also referencing variables, which we need to define in &lt;code&gt;variables.tf&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;variable &lt;span class="s2"&gt;"instance_name"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Instance Name for EC2."&lt;/span&gt;
  &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; string
  default     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

variable &lt;span class="s2"&gt;"instance_type"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Instance Type for EC2."&lt;/span&gt;
  &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; string
  default     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then lastly we need to define our &lt;code&gt;outputs.tf&lt;/code&gt; which will be used to output the instance id and ip address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;output &lt;span class="s2"&gt;"instance_id"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  value &lt;span class="o"&gt;=&lt;/span&gt; aws_instance.ec2.id
&lt;span class="o"&gt;}&lt;/span&gt;

output &lt;span class="s2"&gt;"ip"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  value &lt;span class="o"&gt;=&lt;/span&gt; aws_instance.ec2.public_ip
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy our EC2 with Terraform
&lt;/h2&gt;

&lt;p&gt;Now that our infrastructure has been defined as code, we can first initialise terraform which will initialise the backend and download all the providers that has been defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that has done we can run a “plan” which will show us what Terraform will deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now terraform will show us the difference in what we have defined, and what is actually in AWS, as we know its a new account with zero infrastructure, the diff should show us that it needs to create a EC2 instance.&lt;/p&gt;

&lt;p&gt;The response from the &lt;code&gt;terraform plan&lt;/code&gt; shows us the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  &lt;span class="c"&gt;# aws_instance.ec2 will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"ec2"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + ami                                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-0f56955469757e5aa"&lt;/span&gt;
      + arn                                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + instance_type                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
      + key_name                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + private_ip                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + public_ip                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + security_groups                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + subnet_id                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tags                                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          + &lt;span class="s2"&gt;"Name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test-ec2-instance"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      + tags_all                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          + &lt;span class="s2"&gt;"Name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test-ec2-instance"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      + vpc_security_group_ids               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + instance_id &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
  + ip          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see terraform has looked up the AMI ID using the data source, and we can see that terraform will provision 1 resource which is a EC2 instance. Once we hare happy with the plan, we can run a apply which will show us the same but this time prompt us if we want to proceed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only &lt;span class="s1"&gt;'yes'&lt;/span&gt; will be accepted to approve.

  Enter a value: &lt;span class="nb"&gt;yes

&lt;/span&gt;aws_instance.ec2: Creating...
aws_instance.ec2: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;10s elapsed]
aws_instance.ec2: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;20s elapsed]
aws_instance.ec2: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;30s elapsed]
aws_instance.ec2: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 35s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i-005c08b899229fff0]

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

instance_id &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"i-005c08b899229fff0"&lt;/span&gt;
ip &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"34.253.196.167"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we can see our EC2 instance was provisioned and our outputs returned the instance id as well as the public ip address.&lt;/p&gt;

&lt;p&gt;We can also confirm this by looking at the AWS EC2 Console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N2EQJQzw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/82b4d742-1c45-4d21-8766-10a5c0d074a1" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N2EQJQzw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/ruanbekker/ruanbekker.github.io/assets/567298/82b4d742-1c45-4d21-8766-10a5c0d074a1" alt="aws-ec2-instances-in-console" width="800" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that Terraform Configuration is idempotent, so when we run a terraform apply again, terraform will check what we have defined as what we want our desired infrastructure to be like, and what we actually have in our AWS Account, and since we haven’t made any changes there should be no changes.&lt;/p&gt;

&lt;p&gt;We can run a terraform apply to validate that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can see the response shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;data.aws_vpc.selected: Reading...
data.aws_ami.latest_ubuntu: Reading...
data.aws_ami.latest_ubuntu: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ami-0f56955469757e5aa]
data.aws_vpc.selected: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-063d7ac3124053dfa]
data.aws_subnet.selected: Reading...
data.aws_subnet.selected: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;subnet-0b7acd7593611c1bb]
aws_instance.ec2: Refreshing state... &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i-005c08b899229fff0]

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 0 added, 0 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;Destroy the infrastructure that we provisioned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will show us what terraform will destroy, then upon confirming we should see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Plan: 0 to add, 0 to change, 1 to destroy.

Changes to Outputs:
  - instance_id &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"i-005c08b899229fff0"&lt;/span&gt; -&amp;gt; null
  - ip          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"34.253.196.167"&lt;/span&gt; -&amp;gt; null

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only &lt;span class="s1"&gt;'yes'&lt;/span&gt; will be accepted to confirm.

  Enter a value: &lt;span class="nb"&gt;yes

&lt;/span&gt;aws_instance.ec2: Destroying... &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i-005c08b899229fff0]
aws_instance.ec2: Still destroying... &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i-005c08b899229fff0, 10s elapsed]
aws_instance.ec2: Still destroying... &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i-005c08b899229fff0, 20s elapsed]
aws_instance.ec2: Still destroying... &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i-005c08b899229fff0, 30s elapsed]
aws_instance.ec2: Destruction &lt;span class="nb"&gt;complete &lt;/span&gt;after 31s

Destroy &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 1 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you followed along and you also want to clean up the AWS IAM user, head over to the AWS IAM Console and delete the “medium-terraform” IAM User.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed this post, I will be posting more terraform related content.&lt;/p&gt;

&lt;p&gt;Should you want to reach out to me, you can follow me on Twitter at &lt;a href="https://twitter.com/ruanbekker"&gt;@ruanbekker&lt;/a&gt; or check out my website at &lt;a href="https://ruan.dev"&gt;https://ruan.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>aws</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Run a AMD64 Bit Linux VM on a Mac M1</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Fri, 26 May 2023 21:16:08 +0000</pubDate>
      <link>https://dev.to/ruanbekker/how-to-run-a-amd64-bit-linux-vm-on-a-mac-m1-51cp</link>
      <guid>https://dev.to/ruanbekker/how-to-run-a-amd64-bit-linux-vm-on-a-mac-m1-51cp</guid>
      <description>&lt;p&gt;This tutorial will show you how you can run 64bit Ubuntu Linux Virtual Machines on a Apple Mac M1 arm64 architecture macbook using &lt;a href="https://github.com/utmapp/UTM" rel="noopener noreferrer"&gt;UTM&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Head over to their &lt;a href="https://docs.getutm.app/installation/ios/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and download the &lt;code&gt;UTM.dmg&lt;/code&gt; file and install it, once it is installed and you have opened UTM, you should see this screen:&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%2Fb062v9rc04upi9p85u8m.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%2Fb062v9rc04upi9p85u8m.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Virtual Machine
&lt;/h2&gt;

&lt;p&gt;In my case I would like to run a Ubuntu VM, so head over to the &lt;a href="https://ubuntu.com/download/server" rel="noopener noreferrer"&gt;Ubuntu Server Download&lt;/a&gt; page and download the version of choice, I will be downloading Ubuntu Server 22.04, once you have your ISO image downloaded, you can head over to the next step which is to "Create a New Virtual Machine":&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%2Frlhn1kedtjtm65as3kub.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%2Frlhn1kedtjtm65as3kub.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will select "Emulate" as I want to run a amd64 bit architecture, then select "Linux":&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%2Fxskqxjqza42kfvwx15l8.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%2Fxskqxjqza42kfvwx15l8.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next step we want to select the Ubuntu ISO image that we downloaded, which we want to use to boot our VM from:&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%2Frmjljh4l2qfpr7obrhpf.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%2Frmjljh4l2qfpr7obrhpf.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Browse and select the image that you downloaded, once you selected it, it should show something like this:&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%2Ftx2x4wx8glybvvisg6u2.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%2Ftx2x4wx8glybvvisg6u2.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select continue, then select the architecture to &lt;code&gt;x86_64&lt;/code&gt;, the system I kept on defaults and the memory I have set to &lt;code&gt;2048MB&lt;/code&gt; and cores to &lt;code&gt;2&lt;/code&gt; but that is just my preference:&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%2Fs4dvcg5zchqaqb95y8fb.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%2Fs4dvcg5zchqaqb95y8fb.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next screen is to configure storage, as this is for testing I am setting mine to &lt;code&gt;8GB&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%2Fym6gddxzdjwct52d5kz9.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%2Fym6gddxzdjwct52d5kz9.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next screen is shared directories, this is purely optional, I have created a directory for this:&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;mkdir&lt;/span&gt; ~/utm


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Which I've then defined for a shared directory, but this depends if you need to have shared directories from your local workstation. &lt;/p&gt;

&lt;p&gt;The next screen is a summary of your choices and you can name your vm here:&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%2Fp5928nxxc4mqw72frgg0.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%2Fp5928nxxc4mqw72frgg0.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you are happy select save, and you should see something like this:&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%2Fr2ewdfyhy1o1ub25eu4l.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%2Fr2ewdfyhy1o1ub25eu4l.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then select the play button to start your VM.&lt;/p&gt;

&lt;p&gt;The console should appear and you can select install or try this vm:&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%2Feb8k20awz418ob3hd9s4.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%2Feb8k20awz418ob3hd9s4.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will start the installation process of a Linux Server:&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%2Fewilfddjei0prs81jkqi.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%2Fewilfddjei0prs81jkqi.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can select the options that you would like, I would just recommend to ensure that you select &lt;code&gt;Install OpenSSH Server&lt;/code&gt; so that you can connect to your VM via SSH.&lt;/p&gt;

&lt;p&gt;Once you get to this screen:&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%2Fbmsj1bf4fdbw09k8i9r0.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%2Fbmsj1bf4fdbw09k8i9r0.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The installation process is busy and you will have to wait a couple of minutes for it to complete. Once you see the following screen the installation is complete:&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%2F5x6ov9q08xapuq1b2qvs.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%2F5x6ov9q08xapuq1b2qvs.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the right hand side select the circle, then select CD/DVD and select the ubuntu iso and select eject:&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%2F5jqi5s3qr4zo5j087cm2.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%2F5jqi5s3qr4zo5j087cm2.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting your VM
&lt;/h2&gt;

&lt;p&gt;Then power off the guest and power on again, then you should get a console login, then you can proceed to login, and view the ip address:&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%2Ffzowtx183xztw6ko28xk.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%2Ffzowtx183xztw6ko28xk.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SSH to your VM
&lt;/h2&gt;

&lt;p&gt;Now from your terminal you should be able to ssh to the VM:&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%2Fktu5kqry5yuxc7n7gcfq.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%2Fktu5kqry5yuxc7n7gcfq.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also verify that we are running a 64bit vm, by running &lt;code&gt;uname --processor&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%2Fehloa4073ocds4844744.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%2Fehloa4073ocds4844744.png" alt="linux-64bit-vms-on-apple-mac-m1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;Thanks for reading, feel free to check out my &lt;a href="https://ruan.dev/" rel="noopener noreferrer"&gt;website&lt;/a&gt;, feel free to subscribe to my &lt;a href="http://digests.ruanbekker.com/?via=ruanbekker-blog" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; or follow me at &lt;a href="https://twitter.com/ruanbekker" rel="noopener noreferrer"&gt;@ruanbekker&lt;/a&gt; on Twitter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linktree: &lt;a href="https://go.ruan.dev/links" rel="noopener noreferrer"&gt;https://go.ruan.dev/links&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Patreon: &lt;a href="https://go.ruan.dev/patreon" rel="noopener noreferrer"&gt;https://go.ruan.dev/patreon&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>virtualmachine</category>
      <category>arm</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dasel - jq for yaml json and toml</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Sat, 15 Apr 2023 23:32:05 +0000</pubDate>
      <link>https://dev.to/ruanbekker/dasel-jq-for-yaml-json-and-toml-1hhf</link>
      <guid>https://dev.to/ruanbekker/dasel-jq-for-yaml-json-and-toml-1hhf</guid>
      <description>&lt;p&gt;I stumbled upon a great tool called &lt;a href="https://github.com/TomWright/dasel"&gt;Dasel&lt;/a&gt; which enables you to query and modify data structures using selector strings.&lt;/p&gt;

&lt;p&gt;I was working on a Rust project and wanted a way to query Toml files, similar to how you would use &lt;a href="https://github.com/stedolan/jq"&gt;jq&lt;/a&gt; for json data.&lt;/p&gt;

&lt;p&gt;Dasel was exactly what I was looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;If you are on Mac you can use homebrew to install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;dasel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are on Linux you can install Dasel using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://github.com/TomWright/dasel/releases/download/v2.1.2/dasel_linux_amd64
&lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; root &lt;span class="nt"&gt;-g&lt;/span&gt; root &lt;span class="nt"&gt;-m&lt;/span&gt; 0755 dasel_linux_amd64 /usr/bin/dasel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  YAML
&lt;/h2&gt;

&lt;p&gt;For the first demonstration we will use the following &lt;code&gt;deployment.yaml&lt;/code&gt; 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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;nginx-deployment&lt;/span&gt;
&lt;span class="na"&gt;spec&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;2&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&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;nginx:1.15.0&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;nginx&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="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say we wanted to query the number of replicas:&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;dasel &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml &lt;span class="nt"&gt;-r&lt;/span&gt; yaml &lt;span class="s1"&gt;'.spec.replicas'&lt;/span&gt;                                                     
2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we wanted to view the image:&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;dasel &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml &lt;span class="nt"&gt;-r&lt;/span&gt; yaml &lt;span class="s1"&gt;'.spec.template.spec.containers.[0].image'&lt;/span&gt;
nginx:1.14.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use &lt;code&gt;put&lt;/code&gt; to query and write information back to the yaml file. In this example we will update the container image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dasel put &lt;span class="nt"&gt;-t&lt;/span&gt; string &lt;span class="nt"&gt;-v&lt;/span&gt; nginx:1.15.0 &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml &lt;span class="nt"&gt;-r&lt;/span&gt; yaml &lt;span class="s1"&gt;'.spec.template.spec.containers.[0].image'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To view if the image was updated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dasel &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml &lt;span class="nt"&gt;-r&lt;/span&gt; yaml &lt;span class="s1"&gt;'.spec.template.spec.containers.[0].image'&lt;/span&gt;                           
nginx:1.15.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  TOML
&lt;/h2&gt;

&lt;p&gt;For toml configuration, lets say the file we are working with is &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"helloworld"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2021"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To view the name:&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;dasel &lt;span class="nt"&gt;-f&lt;/span&gt; Cargo.toml &lt;span class="nt"&gt;-r&lt;/span&gt; toml &lt;span class="s1"&gt;'.package.name'&lt;/span&gt;
helloworld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  JSON
&lt;/h2&gt;

&lt;p&gt;For JSON data, we can query it like this:&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"names": [{"name":{"first":"Tom","last":"Wright"}}]}'&lt;/span&gt; | dasel &lt;span class="nt"&gt;-r&lt;/span&gt; json &lt;span class="s1"&gt;'names.[0].name.first'&lt;/span&gt;
Tom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also use the filter function:&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"names": [{"name":"Tom","last":"Wright"},{"name":"John","last":"Wrong"}]}'&lt;/span&gt; | dasel &lt;span class="nt"&gt;-r&lt;/span&gt; json &lt;span class="s1"&gt;'names.all().filter(name)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>devops</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Create a Discord Bot in Python</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Mon, 13 Jun 2022 06:55:36 +0000</pubDate>
      <link>https://dev.to/ruanbekker/create-a-discord-bot-in-python-din</link>
      <guid>https://dev.to/ruanbekker/create-a-discord-bot-in-python-din</guid>
      <description>&lt;p&gt;In this tutorial we will develop our own &lt;strong&gt;&lt;a href="https://discord.com/"&gt;Discord&lt;/a&gt;&lt;/strong&gt; bot using &lt;strong&gt;Python&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The source code for this bot will be stored in my &lt;a href="https://github.com/ruanbekker/discord-minecraft-python-bot"&gt;github repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  About the bot
&lt;/h3&gt;

&lt;p&gt;First we will create a basic discord bot that will greet the message sender, and then we will create a Minecraft Bot, that will enable us to do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:: Bot Usage ::
!mc help : shows help
!mc serverusage : shows system load in percentage
!mc serverstatus : shows if the server is online or offline
!mc whoisonline : shows who is online at the moment

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's get into it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;Create a python virtual environment and install the dependent packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python3 -m virtualenv .venv
$ source .venv/bin/activate
$ pip install discord
$ pip install python-dotenv

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the Discord Application
&lt;/h3&gt;

&lt;p&gt;We first need to create the application on discord and retrieve a token that our python app will require.&lt;/p&gt;

&lt;p&gt;Create a application on discord:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://discordapp.com/developers/applications"&gt;https://discordapp.com/developers/applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should see:&lt;/p&gt;

&lt;p&gt;Click "New Application" and provide it a name:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/tails"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Yvwmmms--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.devdojo.com/images/january2021/970x901.jpg" alt="" width="800" height="74"&gt;Checkout our latest product - the ultimate tailwindcss page creator 🚀&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you create the application you will get a screen to upload a logo, provide a description and most importantly get your application id as well as your public key:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_B6UXJs---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/165911250-0fd11a0b-b851-4d65-a898-7049dd73aa60.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_B6UXJs---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/165911250-0fd11a0b-b851-4d65-a898-7049dd73aa60.png" alt="image" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then select the Bot section:&lt;/p&gt;

&lt;p&gt;Then select "Add Bot":&lt;/p&gt;

&lt;p&gt;Select OAuth2 and select the "bot" scope:&lt;/p&gt;

&lt;p&gt;At the bottom of the page it will provide you with a URL that looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://discord.com/api/oauth2/authorize?client_id=xxxxxxxxxxx&amp;amp;permissions=0&amp;amp;scope=bot

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the link in your browser and authorize the bot to your server of choice:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bM2ravmW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/165917380-6e8fbbed-9237-4017-a8bd-c27d58bcdc6d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bM2ravmW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/165917380-6e8fbbed-9237-4017-a8bd-c27d58bcdc6d.png" alt="image" width="800" height="1241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click authorize, and you should see your bot appearing on Discord:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AVMgv6Ll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/165917760-d8c132e9-18d4-4428-b551-c895d4a5102c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AVMgv6Ll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/165917760-d8c132e9-18d4-4428-b551-c895d4a5102c.png" alt="image" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Developing the Discord Bot
&lt;/h3&gt;

&lt;p&gt;Now we will be building our python discord bot, head back to the "Bot" section and select "Reset Token", then copy and store the token value to a file &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DISCORD_TOKEN=xxxxxxxxx

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in our current working directory, we should have a file &lt;code&gt;.env&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat .env
DISCORD_TOKEN=your-unique-token-value-will-be-here

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this demonstration, I will create a private channel in discord called &lt;code&gt;minecraft-test&lt;/code&gt; and add the bot &lt;code&gt;MinecraftBot&lt;/code&gt; to the channel (this is only for testing, after testing you can add your bot to your other channels for other people to use):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_02ut6Ud--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/166233812-2596960b-5142-4ad1-809e-96d884ea5c58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_02ut6Ud--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/166233812-2596960b-5142-4ad1-809e-96d884ea5c58.png" alt="image" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For our first test, a basic bot, where we would like to type &lt;code&gt;hello&lt;/code&gt; and the bot should greet us by our username, in our &lt;code&gt;mc_discord_bot.py&lt;/code&gt; file we will have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import discord
import os
from dotenv import load_dotenv

BOT_NAME = "MinecraftBot"

load_dotenv()
DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")

bot = discord.Client()

@bot.event
async def on_ready():
    print(f'{bot.user} has logged in.')

@bot.event
async def on_message(message):
    if message.author == bot.user:
        return
    if message.content == 'hello':
        await message.channel.send(f'Hey {message.author}')
    if message.content == 'goodbye':
        await message.channel.send(f'Goodbye {message.author}')

bot.run(DISCORD_TOKEN)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the bot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python mc_discord_bot.py
MinecraftBot has logged in.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when we type &lt;code&gt;hello&lt;/code&gt; and &lt;code&gt;goodbye&lt;/code&gt; you can see our bot responds on those values:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ew1uFzDE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/166235388-7240a66c-2be4-4343-8f36-398077c4fcf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ew1uFzDE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/166235388-7240a66c-2be4-4343-8f36-398077c4fcf6.png" alt="image" width="800" height="833"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we tested our bot, we can clear the &lt;code&gt;mc_discord_bot.py&lt;/code&gt; and write our minecraft bot, the requirements of this bot is simple, but we would like the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use the command &lt;code&gt;!mc&lt;/code&gt; to trigger our bot and subcommands for what we want&lt;/li&gt;
&lt;li&gt;able to see who is playing minecraft on our server at the moment&lt;/li&gt;
&lt;li&gt;able to get the status if the minecraft server is online&lt;/li&gt;
&lt;li&gt;able to get the server load percentage (as the bot runs on the minecraft server)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is our complete &lt;code&gt;mc_discord_bot.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import discord
from discord.ext import commands
import requests
import os
from dotenv import load_dotenv
import random
import multiprocessing

# Variables
BOT_NAME = "MinecraftBot"
load_dotenv()
DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")

minecraft_server_url = "lightmc.fun" # this is just an example, and you should use your own minecraft server

bot_help_message = """
:: Bot Usage ::
`!mc help` : shows help
`!mc serverusage` : shows system load in percentage
`!mc serverstatus` : shows if the server is online or offline
`!mc whoisonline` : shows who is online at the moment
"""

available_commands = ['help', 'serverusage', 'serverstatus', 'whoisonline']

# Set the bot command prefix
bot = commands.Bot(command_prefix="!")

# Executes when the bot is ready
@bot.event
async def on_ready():
    print(f'{bot.user} succesfully logged in!')

# Executes whenever there is an incoming message event
@bot.event
async def on_message(message):
    print(f'Guild: {message.guild.name}, User: {message.author}, Message: {message.content}')
    if message.author == bot.user:
        return

    if message.content == '!mc':
        await message.channel.send(bot_help_message)

    if 'whosonline' in message.content:
        print(f'{message.author} used {message.content}')
    await bot.process_commands(message)

# Executes when the command mc is used and we trigger specific functions
# when specific arguments are caught in our if statements
@bot.command()
async def mc(ctx, arg):
    if arg == 'help':
        await ctx.send(bot_help_message)

    if arg == 'serverusage':
        cpu_count = multiprocessing.cpu_count()
        one, five, fifteen = os.getloadavg()
        load_percentage = int(five / cpu_count * 100)
        await ctx.send(f'Server load is at {load_percentage}%')

    if arg == 'serverstatus':
        response = requests.get(f'https://api.mcsrvstat.us/2/{minecraft_server_url}').json()
        server_status = response['online']
        if server_status == True:
            server_status = 'online'
        await ctx.send(f'Server is {server_status}')

    if arg == 'whoisonline':
        response = requests.get('https://api.mcsrvstat.us/2/{minecraft_server_url}').json()
        players_status = response['players']
        if players_status['online'] == 0:
            players_online_message = 'No one is online'
        if players_status['online'] == 1:
            players_online_username = players_status['list'][0]
            players_online_message = f'1 player is online: {players_online_username}'
        if players_status['online'] &amp;gt; 1:
            po = players_status['online']
            players_online_usernames = players_status['list']
            joined_usernames = ", ".join(players_online_usernames)
            players_online_message = f'{po} players are online: {joined_usernames}'
        await ctx.send(f'{players_online_message}')

bot.run(DISCORD_TOKEN)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we can start our bot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python mc_discord_bot.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can run our help command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;!mc help

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will prompt our help message, and then test out the others:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xrlz9kmr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/166237617-c2df1dd1-99bc-4558-8eb8-b1159e850836.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xrlz9kmr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/567298/166237617-c2df1dd1-99bc-4558-8eb8-b1159e850836.png" alt="image" width="800" height="1104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;Thank you to the following authors, which really helped me doing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.freecodecamp.org/news/create-a-discord-bot-with-python/"&gt;www.freecodecamp.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://betterprogramming.pub/coding-a-discord-bot-with-python-64da9d6cade7"&gt;betterprogramming.pub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/codesphere/create-a-discord-bot-in-minutes-with-python-2jgp"&gt;dev.to/codesphere&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Thank You
&lt;/h3&gt;

&lt;p&gt;Thanks for reading, if you like my content, check out my &lt;strong&gt;&lt;a href="https://ruan.dev"&gt;website&lt;/a&gt;&lt;/strong&gt;, read my &lt;strong&gt;&lt;a href="http://digests.ruanbekker.com/?via=ruanbekker-blog"&gt;newsletter&lt;/a&gt;&lt;/strong&gt; or follow me at &lt;strong&gt;&lt;a href="https://twitter.com/ruanbekker"&gt;@ruanbekker&lt;/a&gt;&lt;/strong&gt; on Twitter.&lt;/p&gt;

&lt;p&gt;The source code for this bot will be stored in my github repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ruanbekker/discord-minecraft-python-bot"&gt;github.com/ruanbekker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've started a brand new Discord server, not much happening at the moment, but planning to share and distribute tech content and a place for like minded people to hang out. If that's something you are interested in, feel free to join on &lt;strong&gt;&lt;a href="https://discord.gg/bPmc4Stchd"&gt;this link&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>discord</category>
      <category>bot</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Setup a Self-Hosted Git Service with Gitea</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Fri, 22 Oct 2021 09:18:20 +0000</pubDate>
      <link>https://dev.to/ruanbekker/setup-a-self-hosted-git-service-with-gitea-11ce</link>
      <guid>https://dev.to/ruanbekker/setup-a-self-hosted-git-service-with-gitea-11ce</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;a href="https://gitea.io/en-us/" rel="noopener noreferrer"&gt;Gitea&lt;/a&gt;&lt;/strong&gt; is a self-hosted &lt;a href="https://en.wikipedia.org/wiki/Git" rel="noopener noreferrer"&gt;git&lt;/a&gt; service written in Go, it's super lightweight to run and supports ARM architectures as well, so you can run it on a Raspberry Pi as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are we doing today
&lt;/h2&gt;

&lt;p&gt;In this tutorial we will be setting up a self hosted version control repository with &lt;strong&gt;&lt;a href="https://gitea.io/en-us/" rel="noopener noreferrer"&gt;Gitea&lt;/a&gt;&lt;/strong&gt; on &lt;strong&gt;&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/strong&gt; using &lt;strong&gt;&lt;a href="https://traefik.io/" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt;&lt;/strong&gt; as our Load Balancer and SSL terminations for LetsEncrypt certificates.&lt;/p&gt;

&lt;p&gt;We will then create a example git repository, add our ssh key to our account and clone our repository using ssh, change some code, commit and push to our repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;p&gt;I will assume that you have &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;docker&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;docker-compose&lt;/a&gt; installed. If you need more info on &lt;a href="https://traefik.io/" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt; you can have a look at their website, but I have also written a post on &lt;a href="https://containers.fan/posts/setup-traefik-v2-docker-compose/" rel="noopener noreferrer"&gt;setting up Traefik v2&lt;/a&gt; in detail, but we will touch on that in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Details
&lt;/h2&gt;

&lt;p&gt;I have 1 DNS entry set to the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traefik: &lt;code&gt;traefik.rbkr.xyz&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Gitea: &lt;code&gt;git.rbkr.xyz&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Accessing our service will be done over &lt;code&gt;HTTPS&lt;/code&gt; on port &lt;code&gt;443&lt;/code&gt;, and for cloning over &lt;code&gt;SSH&lt;/code&gt;, the port will be set to &lt;code&gt;222&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Directory Structure
&lt;/h2&gt;

&lt;p&gt;Create the &lt;code&gt;gitea&lt;/code&gt; directory which will be our docker compose project directory:&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;mkdir &lt;/span&gt;gitea
&lt;span class="nb"&gt;cd &lt;/span&gt;gitea


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Create the traefik directory for &lt;code&gt;acme.json&lt;/code&gt; where certificate data will be stored, create the file and change permissions on the 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;touch &lt;/span&gt;traefik/acme.json
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 traefik/acme.json


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Traefik
&lt;/h2&gt;

&lt;p&gt;Open the &lt;code&gt;docker-compose.yml&lt;/code&gt; and add the first bit which will Traefik, ensure that you replace the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;me@example.com&lt;/code&gt; with your email under &lt;code&gt;certificatesResolvers.letsencrypt.acme.email&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;traefik.rbkr.xyz&lt;/code&gt; with your fqdn for traefik under &lt;code&gt;traefik.http.routers.api.rule=Host()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The section for traefik:&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.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;gitea-traefik&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;traefik:2.4&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;gitea-traefik&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="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;./traefik/acme.json:/acme.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&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;public&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="s1"&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.rule=Host(`traefik.rbkr.xyz`)'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.entrypoints=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.service=api@internal'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.tls=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.tls.certresolver=letsencrypt'&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="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;443:443&lt;/span&gt;
    &lt;span class="na"&gt;command&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;--api'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--providers.docker=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--providers.docker.exposedByDefault=false'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.address=:80'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.http.redirections.entrypoint.to=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.http.redirections.entrypoint.scheme=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.https=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.https.address=:443'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.email=me@example.com`'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.storage=acme.json'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--log=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--log.level=INFO'&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&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;public&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;public&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="na"&gt;We can start traefik so long&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="s"&gt;bash&lt;/span&gt;
&lt;span class="s"&gt;docker-compose up -d&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="c1"&gt;## Gitea&lt;/span&gt;

&lt;span class="s"&gt;Now we will add the Gitea components, I have opted in for the gitea service and a redis cache and will be making use of sqlite as this is just a demonstration. For non-test environments, you can have a look at MySQL or Postres&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://docs.gitea.io/en-us/install-with-docker/#databases&lt;/span&gt;

&lt;span class="na"&gt;Review the following configuration options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;DOMAIN` and `SSH_DOMAIN` (this will be used in your clone urls)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;ROOT_URL` (this is set to use the HTTPS protocol, including my domain)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;SSH_LISTEN_PORT` (this is the port listening for SSH inside the container)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;SSH_PORT` (this is the port we are exposing from outside, which will be replaced in the clone url)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;DB_TYPE` (Im using sqlite for this example)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.gitea.rule=Host()` (the host header to reach gitea via web)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;./data/gitea` (I am persisting the data in my local working directory under the given path)&lt;/span&gt;

&lt;span class="na"&gt;The gitea portion of the `docker-compose.yml`&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="s"&gt;yaml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&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.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="s"&gt;gitea&lt;/span&gt;&lt;span class="err"&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;gitea&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;gitea/gitea:${GITEA_VERSION:-1.14.5}&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="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;gitea-traefik&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;service_started&lt;/span&gt;
      &lt;span class="na"&gt;gitea-cache&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;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;APP_NAME="Gitea"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;USER_UID=1000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;USER_GID=1000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;USER=git&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RUN_MODE=prod&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DOMAIN=git.rbkr.xyz&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SSH_DOMAIN=git.rbkr.xyz&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HTTP_PORT=3000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ROOT_URL=https://git.rbkr.xyz&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SSH_PORT=222&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SSH_LISTEN_PORT=22&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_TYPE=sqlite3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GITEA__cache__ENABLED=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GITEA__cache__ADAPTER=redis&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GITEA__cache__HOST=redis://gitea-cache:6379/0?pool_size=100&amp;amp;idle_timeout=180s&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GITEA__cache__ITEM_TTL=24h&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;222:22"&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;public&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;./data/gitea:/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/timezone:/etc/timezone:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/localtime:/etc/localtime:ro&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="s2"&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.gitea.rule=Host(`git.rbkr.xyz`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.gitea.entrypoints=https"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.gitea.tls.certresolver=letsencrypt"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.gitea.service=gitea-service"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.gitea-service.loadbalancer.server.port=3000"&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&lt;/span&gt;

&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;gitea-cache&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;gitea-cache&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;redis:6-alpine&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="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;public&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis-cli"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="na"&gt;So my complete `docker-compose.yml` will look like the following&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="s"&gt;yaml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&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.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;gitea-traefik&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;traefik:2.4&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;gitea-traefik&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="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;./traefik/acme.json:/acme.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&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;public&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="s1"&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.rule=Host(`traefik.rbkr.xyz`)'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.entrypoints=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.service=api@internal'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.tls=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.api.tls.certresolver=letsencrypt'&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="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;443:443&lt;/span&gt;
    &lt;span class="na"&gt;command&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;--api'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--providers.docker=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--providers.docker.exposedByDefault=false'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.address=:80'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.http.redirections.entrypoint.to=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.http.redirections.entrypoint.scheme=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.https=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.https.address=:443'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.email=me@example.com'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.storage=acme.json'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--log=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--log.level=INFO'&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&lt;/span&gt;

  &lt;span class="na"&gt;gitea&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;gitea&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;gitea/gitea:${GITEA_VERSION:-1.14.5}&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="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;gitea-traefik&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;service_started&lt;/span&gt;
      &lt;span class="na"&gt;gitea-cache&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;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;APP_NAME="Gitea"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;USER_UID=1000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;USER_GID=1000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;USER=git&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RUN_MODE=prod&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DOMAIN=git.rbkr.xyz&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SSH_DOMAIN=git.rbkr.xyz&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HTTP_PORT=3000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ROOT_URL=https://git.rbkr.xyz&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SSH_PORT=222&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SSH_LISTEN_PORT=22&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_TYPE=sqlite3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GITEA__cache__ENABLED=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GITEA__cache__ADAPTER=redis&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GITEA__cache__HOST=redis://gitea-cache:6379/0?pool_size=100&amp;amp;idle_timeout=180s&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GITEA__cache__ITEM_TTL=24h&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;222:22"&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;public&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;./data/gitea:/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/timezone:/etc/timezone:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/localtime:/etc/localtime:ro&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="s2"&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.gitea.rule=Host(`git.rbkr.xyz`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.gitea.entrypoints=https"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.gitea.tls.certresolver=letsencrypt"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.gitea.service=gitea-service"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.gitea-service.loadbalancer.server.port=3000"&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&lt;/span&gt;

  &lt;span class="na"&gt;gitea-cache&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;gitea-cache&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;redis:6-alpine&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="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;public&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis-cli"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&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;public&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;public&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="s"&gt;Once your configuration is updated, start gitea&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="s"&gt;bash&lt;/span&gt;
&lt;span class="s"&gt;docker-compose up -d&lt;/span&gt;

&lt;span class="s"&gt;Creating network "public" with the default driver&lt;/span&gt;
&lt;span class="s"&gt;Creating gitea-traefik ... done&lt;/span&gt;
&lt;span class="s"&gt;Creating gitea-cache   ... done&lt;/span&gt;
&lt;span class="s"&gt;Creating gitea         ... done&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="s"&gt;Once the containers has started, verify that they are all up&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="s"&gt;bash&lt;/span&gt;
&lt;span class="s"&gt;docker-compose ps&lt;/span&gt;
    &lt;span class="s"&gt;Name                   Command                  State                                       Ports&lt;/span&gt;
&lt;span class="s"&gt;--------------------------------------------------------------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="s"&gt;gitea           /usr/bin/entrypoint /bin/s ...   Up             0.0.0.0:222-&amp;gt;22/tcp,:::222-&amp;gt;22/tcp, 3000/tcp&lt;/span&gt;
&lt;span class="s"&gt;gitea-cache     docker-entrypoint.sh redis ...   Up (healthy)   6379/tcp&lt;/span&gt;
&lt;span class="s"&gt;gitea-traefik   /entrypoint.sh --api --pro ...   Up             0.0.0.0:443-&amp;gt;443/tcp,:::443-&amp;gt;443/tcp, 0.0.0.0:80-&amp;gt;80/tcp,:::80-&amp;gt;80/tcp&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="c1"&gt;## Installation and Configuration&lt;/span&gt;

&lt;span class="s"&gt;Head over to the `ROOT_URL` of your gitea installation, in my case it looked like the following&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![gitea-container](https://user-images.githubusercontent.com/567298/138003899-63d0cfd8-ca97-4e9f-b9d6-224987105d03.png)&lt;/span&gt;

&lt;span class="s"&gt;If you are not automatically redirected to register an account, select "Register" in the top right side&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![gitea-register](https://user-images.githubusercontent.com/567298/138001952-5bf32a22-5287-4d4f-b852-6860dd6417af.png)&lt;/span&gt;

&lt;span class="s"&gt;If you would like to make use of email, configure your email settings here&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![gitea-email](https://user-images.githubusercontent.com/567298/138002011-f261870d-2fcc-4809-8d12-8317fa252de3.png)&lt;/span&gt;

&lt;span class="na"&gt;Then configure the admin account&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![gitea-admin-account](https://user-images.githubusercontent.com/567298/138422959-8637e282-7393-4cc2-b942-fe65cf6ea80f.png)&lt;/span&gt;

&lt;span class="s"&gt;Once you are logged in, you should see the following screen&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![image](https://user-images.githubusercontent.com/567298/138002202-818a364c-74a9-4be9-8963-cd151d10d96f.png)&lt;/span&gt;

&lt;span class="c1"&gt;## SSH Key&lt;/span&gt;

&lt;span class="na"&gt;Now we would like to create a SSH key so that we can authorize our git client to pull and push to/from Gitea&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="s"&gt;bash&lt;/span&gt;
&lt;span class="s"&gt;ssh-keygen -f ~/.ssh/gitea-demo -t rsa -C "Gitea-Demo" -q -N ""&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="s"&gt;Then head to your profile, select settings&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![image](https://user-images.githubusercontent.com/567298/138002368-679ca1eb-81a3-4a83-8aaf-e5c182305be4.png)&lt;/span&gt;

&lt;span class="s"&gt;Select the SSH Tab and select "Add Key"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![image](https://user-images.githubusercontent.com/567298/138002437-a43fbd5b-92a6-40b8-abb1-ebe78c8a5c67.png)&lt;/span&gt;

&lt;span class="na"&gt;Head back to your terminal and copy your public ssh key from the key that we created earlier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="s"&gt;bash&lt;/span&gt;
&lt;span class="s"&gt;cat ~/.ssh/gitea-demo.pub&lt;/span&gt;
&lt;span class="s"&gt;ssh-rsa AAAAB[x----redacted----x]/en5QDz3vI18n1u4lrKu1YsTR57YL Gitea-Demo&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="s"&gt;Then paste the public key into the form and add the key, you should then see the key present in gitea&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![image](https://user-images.githubusercontent.com/567298/138002601-be049746-fcb6-46ad-a931-bfa11d1725b2.png)&lt;/span&gt;

&lt;span class="c1"&gt;## Create a Git Repository&lt;/span&gt;

&lt;span class="s"&gt;Now head back to the "Dashboard", then select the "+" sign at the top and create a "New Repository"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![image](https://user-images.githubusercontent.com/567298/138002657-054e9e6a-25bd-4f4a-9bdb-ef368a720e5f.png)&lt;/span&gt;

&lt;span class="s"&gt;From the repo form, I will be naming my repository "hello-world"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![image](https://user-images.githubusercontent.com/567298/138002751-88ff1793-b6e4-4ac4-886d-d4826e152ffa.png)&lt;/span&gt;

&lt;span class="s"&gt;Then I selected "Initialise repository with Readme" and I selected to create the repository&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![image](https://user-images.githubusercontent.com/567298/138002819-69ce233d-c073-4ed4-a714-1762d34f5b47.png)&lt;/span&gt;

&lt;span class="s"&gt;Now when we select the repo, we should see it in the Gitea UI&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![image](https://user-images.githubusercontent.com/567298/138002865-2e9cc6d9-3fea-4f32-91b8-6199063b7d4f.png)&lt;/span&gt;

&lt;span class="s"&gt;To clone the repository via SSH, select the "SSH" button and click copy to clipboard&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![image](https://user-images.githubusercontent.com/567298/138002962-962b746f-619e-4f08-b058-6d8f4d719e89.png)&lt;/span&gt;

&lt;span class="s"&gt;Before we clone the repo on our terminal, let's setup the ssh-agent to be active for 1 hour&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;
&lt;span class="s"&gt;eval $(ssh-agent -t 3600)&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="na"&gt;Then add the ssh key to the ssh-agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;
&lt;span class="s"&gt;ssh-add ~/.ssh/gitea-demo&lt;/span&gt;

&lt;span class="na"&gt;Identity added&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/.ssh/gitea-demo (Gitea-Demo)&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="nv"&gt;*Optional&lt;/span&gt;&lt;span class="s"&gt;:* If you have a non default ssh key, like the above and you don't want to make use of `ssh-agent` you can setup a SSH Config, for example in `~/.ssh/config`&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;
&lt;span class="c1"&gt;# Globals&lt;/span&gt;
&lt;span class="s"&gt;Host *&lt;/span&gt;
  &lt;span class="s"&gt;StrictHostKeyChecking no&lt;/span&gt;
  &lt;span class="s"&gt;UserKnownHostsFile /dev/null&lt;/span&gt;
  &lt;span class="s"&gt;#AddKeysToAgent yes&lt;/span&gt;
  &lt;span class="s"&gt;#IdentityFile ~/.ssh/id_rsa&lt;/span&gt;
  &lt;span class="s"&gt;ServerAliveInterval &lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;
  &lt;span class="s"&gt;ServerAliveCountMax &lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;

&lt;span class="c1"&gt;# Gitea&lt;/span&gt;
&lt;span class="s"&gt;Host git.rbkr.xyz&lt;/span&gt;
  &lt;span class="s"&gt;IdentityFile ~/.ssh/gitea-demo&lt;/span&gt;
  &lt;span class="s"&gt;User git&lt;/span&gt;
  &lt;span class="s"&gt;Port &lt;/span&gt;&lt;span class="m"&gt;222&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="s"&gt;Now let's clone the repository&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;
&lt;span class="s"&gt;git clone ssh://git@git.rbkr.xyz:222/ruanbekker/hello-world.git&lt;/span&gt;

&lt;span class="s"&gt;Cloning into 'hello-world'...&lt;/span&gt;
&lt;span class="na"&gt;Warning&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Permanently added '[git.rbkr.xyz]:222,[95.x.x.x]:222' (ECDSA) to the list of known hosts.&lt;/span&gt;
&lt;span class="na"&gt;remote: Enumerating objects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3, done.&lt;/span&gt;
&lt;span class="na"&gt;remote: Counting objects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100% (3/3), done.&lt;/span&gt;
&lt;span class="na"&gt;remote&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Total 3 (delta 0), reused 0 (delta 0), pack-reused &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="na"&gt;Receiving objects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100% (3/3), done.&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="na"&gt;Change into the directory which we cloned&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;
&lt;span class="s"&gt;cd hello-world&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="s"&gt;Let's update the `README.md` file with any content, then after we saved the file, we can see that the file has been changed&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;
&lt;span class="s"&gt;git status&lt;/span&gt;

&lt;span class="s"&gt;On branch master&lt;/span&gt;
&lt;span class="s"&gt;Your branch is up to date with 'origin/master'.&lt;/span&gt;

&lt;span class="na"&gt;Changes not staged for commit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;(use "git add &amp;lt;file&amp;gt;..." to update what will be committed)&lt;/span&gt;
  &lt;span class="s"&gt;(use "git restore &amp;lt;file&amp;gt;..." to discard changes in working directory)&lt;/span&gt;
    &lt;span class="s"&gt;modified&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;README.md&lt;/span&gt;

&lt;span class="s"&gt;no changes added to commit (use "git add" and/or "git commit -a")&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="s"&gt;Add the file, commit and push to master&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;```&lt;/span&gt;
&lt;span class="s"&gt;git add README.md&lt;/span&gt;
&lt;span class="s"&gt;git commit -m "Update readme for blogpost"&lt;/span&gt;
&lt;span class="s"&gt;git push origin master&lt;/span&gt;

&lt;span class="na"&gt;Writing objects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100% (3/3), 305 bytes | 305.00 KiB/s, done.&lt;/span&gt;
&lt;span class="s"&gt;Total 3 (delta 0), reused 0 (delta 0), pack-reused &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="na"&gt;remote&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;. Processing 1 references&lt;/span&gt;
&lt;span class="na"&gt;remote&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Processed 1 references in total&lt;/span&gt;
&lt;span class="s"&gt;To ssh://git.rbkr.xyz:222/ruanbekker/hello-world.git&lt;/span&gt;
   &lt;span class="s"&gt;7804b67..85550dd  master -&amp;gt; master&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;

&lt;span class="c1"&gt;## View the changes&lt;/span&gt;

&lt;span class="s"&gt;When we head back to the Gitea UI, we can see the README file has been updated, and we can see a git commit sha as well&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![gitea-ui](https://user-images.githubusercontent.com/567298/138003524-0eb7de18-f90d-4671-8d29-1e6614529743.png)&lt;/span&gt;

&lt;span class="s"&gt;In order to see what changed, we can click on the git commit sha&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![gitea-commit](https://user-images.githubusercontent.com/567298/138003595-262b2f43-06dc-4881-8277-bab17b2ef005.png)&lt;/span&gt;

&lt;span class="c1"&gt;## Swagger API&lt;/span&gt;

&lt;span class="na"&gt;Gitea ships with Swagger by default and the endpoint is `/api/swagger` which in my case is accessible via&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://git.rbkr.xyz/api/swagger&lt;/span&gt;

&lt;span class="na"&gt;And it looks like the following&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="kt"&gt;![gitea-swagger](https://user-images.githubusercontent.com/567298/138003950-c9e06f77-3ce6-434e-bf10-a10dcdf68f56.png)&lt;/span&gt;

&lt;span class="c1"&gt;## Thank You&lt;/span&gt;

&lt;span class="s"&gt;I hope this was helpful, I was really impressed with Gitea. If you liked this content, please make sure to share or come say hi on my website or twitter&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;* **Website**&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ruan.dev&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(https://ruan.dev)&lt;/span&gt;
  &lt;span class="na"&gt;* **Twitter**&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;ruanbekker&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(https://twitter.com/ruanbekker)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>git</category>
      <category>docker</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Setup Glitchtip Error Monitoring on Docker</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Mon, 04 Oct 2021 15:02:18 +0000</pubDate>
      <link>https://dev.to/ruanbekker/setup-glitchtip-error-monitoring-on-docker-358</link>
      <guid>https://dev.to/ruanbekker/setup-glitchtip-error-monitoring-on-docker-358</guid>
      <description>&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%2Fiw3uble4k1n1xidmd6ph.jpeg" 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%2Fiw3uble4k1n1xidmd6ph.jpeg" alt="application error monitoring with glitchtip on docker" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://glitchtip.com/" rel="noopener noreferrer"&gt;Glitchtip&lt;/a&gt;&lt;/strong&gt; is a open-source exception monitoring system, it's similar to &lt;strong&gt;&lt;a href="https://sentry.io/welcome/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;&lt;/strong&gt;, which collects errors reported from your applications and helps you discover &lt;strong&gt;errors&lt;/strong&gt; in real time and also helps you to understand the health of your applications. &lt;/p&gt;

&lt;h2&gt;
  
  
  What are we doing today?
&lt;/h2&gt;

&lt;p&gt;In this tutorial we will setup &lt;strong&gt;Glitchtip&lt;/strong&gt; on &lt;strong&gt;Docker&lt;/strong&gt; using &lt;strong&gt;Traefik&lt;/strong&gt; as our Load Balancer and SSL terminations for LetsEncrypt certificates, then we will create a Python Flask application and initiate a error so that we can see how these errors are collected by &lt;strong&gt;Glitchtip&lt;/strong&gt; and we will also add an Webhook Server to demonstrate logging errors to a Webhook Endpoint for further development on errors.&lt;/p&gt;

&lt;p&gt;So that we can see our exceptions in our applications like this:&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%2Fcd4qpfbbubw3veuai3cv.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%2Fcd4qpfbbubw3veuai3cv.png" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;p&gt;I will assume that you have &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;docker&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;docker-compose&lt;/a&gt; installed. If you need more info on &lt;a href="https://traefik.io/" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt; you can have a look at their website, but I have also written a post on setting up &lt;a href="https://containers.fan/posts/setup-traefik-v2-docker-compose/" rel="noopener noreferrer"&gt;Traefik v2&lt;/a&gt; in detail, but we will touch on that in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Source
&lt;/h2&gt;

&lt;p&gt;I will publish the code for this tutorial under my &lt;a href="https://github.com/ruanbekker/docker-glitchtip-traefik" rel="noopener noreferrer"&gt;Github Repository&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ruanbekker/docker-glitchtip-traefik" rel="noopener noreferrer"&gt;https://github.com/ruanbekker/docker-glitchtip-traefik&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Environment Details
&lt;/h2&gt;

&lt;p&gt;I have 3 DNS Entries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Glitchtip: &lt;code&gt;gc.civo.rbkr.xyz&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Flask App: &lt;code&gt;flask-app.civo.rbkr.xyz&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Webhook: &lt;code&gt;flask-webhook.civo.rbkr.xyz&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which all points to the host running Glitchtip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Directory Structure
&lt;/h2&gt;

&lt;p&gt;First create the directory structure for our glitchtip demo and change to the directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir glitchtip-tutorial
cd glitchtip-tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the traefik directory for &lt;code&gt;acme.json&lt;/code&gt; where certificate data will be stored, create the file and change permissions on the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir traefik
touch traefik/acme.json
chmod 600 traefik/acme.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Traefik
&lt;/h2&gt;

&lt;p&gt;Open the &lt;code&gt;docker-compose.yml&lt;/code&gt; and add the first bit which will Traefik, ensure that you replace &lt;code&gt;me@example.com&lt;/code&gt; with your email under &lt;code&gt;certificatesResolvers.letsencrypt.acme.email&lt;/code&gt;:&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;x-logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="nl"&gt;&amp;amp;default-logging&lt;/span&gt;
  &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&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;glitchtip-traefik&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;traefik:2.4&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;glitchtip-traefik&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="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;./traefik/acme.json:/acme.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&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;glitchtip&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="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;443:443&lt;/span&gt;
    &lt;span class="na"&gt;command&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;--api'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--providers.docker=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--providers.docker.exposedByDefault=false'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.address=:80'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.http.redirections.entrypoint.to=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.http.redirections.entrypoint.scheme=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.https=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.https.address=:443'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.email=me@example.com'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.storage=acme.json'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--log=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--log.level=INFO'&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&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;glitchtip&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;glitchtip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now start Traefik so long:&lt;br&gt;
&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;h2&gt;
  
  
  Glitchtip
&lt;/h2&gt;

&lt;p&gt;Now we will add the Glitchtip components, you can refer to their &lt;a href="https://glitchtip.com/documentation/install" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; if you want to read more into these configuration options. Add the following services below &lt;code&gt;glitchtip-traefk&lt;/code&gt;:&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;glitchtip-postgres&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;postgres:13&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;glitchtip-postgres&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="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_HOST_AUTH_METHOD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trust"&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;PGDATA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/postgresql/data/pgdata&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;./data/postgres:/var/lib/postgresql/data&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;glitchtip&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-U"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-redis&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;redis&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;glitchtip-redis&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="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;glitchtip&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis-cli"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&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;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-web&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;glitchtip/glitchtip&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;glitchtip-web&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="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-traefik&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;service_started&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-postgres&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;service_healthy&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-redis&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;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:postgres@glitchtip-postgres:5432/postgres&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://glitchtip-redis:6379/0&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jsdf93892309fhufhr&lt;/span&gt;
      &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;
      &lt;span class="na"&gt;EMAIL_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${EMAIL_URL}"&lt;/span&gt;
      &lt;span class="na"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${EMAIL_FROM_ADDRESS}&lt;/span&gt;
      &lt;span class="na"&gt;GLITCHTIP_DOMAIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gt.civo.rbkr.xyz"&lt;/span&gt;
      &lt;span class="na"&gt;ENABLE_OPEN_USER_REGISTRATION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;True"&lt;/span&gt;
      &lt;span class="na"&gt;GLITCHTIP_MAX_EVENT_LIFE_DAYS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&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;glitchtip&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="s1"&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.rule=Host(`gt.civo.rbkr.xyz`)'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.entrypoints=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.tls=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.tls.certresolver=letsencrypt'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.service=glitchtip-service'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.glitchtip-service.loadbalancer.server.port=8000'&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-worker&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;glitchtip/glitchtip&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;glitchtip-worker&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="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;celery -A glitchtip worker -B -l INFO&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-postgres&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;service_healthy&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-redis&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;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:postgres@glitchtip-postgres:5432/postgres&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://glitchtip-redis:6379/0&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jsdf93892309fhufhr&lt;/span&gt;
      &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;
      &lt;span class="na"&gt;EMAIL_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${EMAIL_URL}"&lt;/span&gt;
      &lt;span class="na"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${EMAIL_FROM_ADDRESS}&lt;/span&gt;
      &lt;span class="na"&gt;GLITCHTIP_DOMAIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gt.civo.rbkr.xyz"&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;glitchtip&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-migrate&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;glitchtip/glitchtip&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;glitchtip-migrate&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-postgres&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;service_healthy&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-redis&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;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./manage.py&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;migrate"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:postgres@glitchtip-postgres:5432/postgres&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://glitchtip-redis:6379/0&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jsdf93892309fhufhr&lt;/span&gt;
      &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;
      &lt;span class="na"&gt;EMAIL_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${EMAIL_URL}"&lt;/span&gt;
      &lt;span class="na"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${EMAIL_FROM_ADDRESS}&lt;/span&gt;
      &lt;span class="na"&gt;GLITCHTIP_DOMAIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gt.civo.rbkr.xyz"&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;glitchtip&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure that you change the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;EMAIL_URL&lt;/code&gt; eg. &lt;code&gt;smtp://apikey:your-password@smtp.sendgrid.net:587&lt;/code&gt; (&lt;a href="https://www.twilio.com/sendgrid/email-api" rel="noopener noreferrer"&gt;sendgrid&lt;/a&gt; offers free mail delivery)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEFAULT_FROM_EMAIL&lt;/code&gt; eg. &lt;code&gt;no-reply@yourdomain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Your FQDN for glitchtip in &lt;code&gt;'traefik.http.routers.glitchtip.rule=Host()'&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once your configuration is updated, start glitchtip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose pull
docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once all the services are started you should be able to access the UI and will look more or less like this:&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%2Flnnfes50j811uyqq09e7.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%2Flnnfes50j811uyqq09e7.png" alt="image" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;ENABLE_OPEN_USER_REGISTRATION&lt;/code&gt; is set to &lt;code&gt;True&lt;/code&gt;, we can register our user:&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%2Fwlhx7qfo7hsf7u50zfg2.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%2Fwlhx7qfo7hsf7u50zfg2.png" alt="image" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you register you will be logged in and on the organization page to create a organization:&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%2Ff5fv085iv6ozlaeofsga.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%2Ff5fv085iv6ozlaeofsga.png" alt="image" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this tutorial, I will be using the name &lt;code&gt;my-org&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%2F897jyhvgd7hcygwz7qsc.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%2F897jyhvgd7hcygwz7qsc.png" alt="image" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will go ahead and create a application in my organisation called &lt;code&gt;flask-service-dev&lt;/code&gt; and I will be selecting Python Flask for this tutorial, and I want to associate this project to a team called &lt;code&gt;engineering&lt;/code&gt;, at the bottom select team and create your team:&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%2F7lqf4ok1aua5v2zix9nu.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%2F7lqf4ok1aua5v2zix9nu.png" alt="image" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the team has been created and selected, select the framework and name your service:&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%2Fzt2rdi22mhry5ap9xuz4.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%2Fzt2rdi22mhry5ap9xuz4.png" alt="image" width="800" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you created your project you will get a getting started guide on how to configure your application:&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%2F0g7vv5ux9i7mvfgr12rh.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%2F0g7vv5ux9i7mvfgr12rh.png" alt="image" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the right hand side you will get the configuration values, which we will use as environment variables on our application that we want to emit errors to glitchtip:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://6ee24c1a446347b99e9574edfe990393@gt.civo.rbkr.xyz/1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://gt.civo.rbkr.xyz/api/1/security/?glitchtip_key=6ee24c1a446347b99e9574edfe990393&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have signed up we can disable &lt;code&gt;ENABLE_OPEN_USER_REGISTRATION&lt;/code&gt; by setting it to &lt;code&gt;False&lt;/code&gt; in the &lt;code&gt;docker-compose.yml&lt;/code&gt;, then run &lt;code&gt;docker-compose up -d&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Optional: Glitchtip is written in Django, so you can also use the following method to manage users and access the admin panel on &lt;code&gt;/admin&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose run glitchtip-migrate ./manage.py createsuperuser
Creating glitchdip_glitchtip-migrate_run ... done
Email: me@example.com
Password:
Password (again):
Superuser created successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Python Flask App
&lt;/h2&gt;

&lt;p&gt;Now we will create the application that we want to monitor for errors on glitchtip. From the &lt;code&gt;glitchtip-tutorial&lt;/code&gt; directory, create the application code and dockerfile for our python flask app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir flask-app
touch flask-app/requirements.txt
touch flask-app/app.py
touch flask-app/Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First add the requirements in &lt;code&gt;flask-app/requirements.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flask==1.1.2
sentry-sdk[flask]
gunicorn==20.0.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next our dockerfile in &lt;code&gt;flask-app/Dockerfile&lt;/code&gt;:&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="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.8-slim&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; requirements.txt /src/requirements.txt&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; run.sh /src/run.sh&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /src/run.sh
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /src/requirements.txt
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; app.py /src/app.py&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/src/run.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our boot script that will start flask using gunicorn in &lt;code&gt;flask-app/run.sh&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;#!/bin/sh&lt;/span&gt;
gunicorn app:app &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--workers&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--threads&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--bind&lt;/span&gt; 0.0.0.0:80 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--capture-output&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--access-logfile&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--error-logfile&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then our application code in &lt;code&gt;flask-app/app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sentry_sdk&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentry_sdk.integrations.flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FlaskIntegration&lt;/span&gt;

&lt;span class="n"&gt;GLITCHTIP_DSN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DSN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;sentry_sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GLITCHTIP_DSN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;integrations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;FlaskIntegration&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DEBUG&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="nd"&gt;@app.before_first_request&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup_logging&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StreamHandler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs enabled&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello, world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/log-error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_error&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logging error with glitchtip&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logged error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/trigger-error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trigger_error&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;division_by_zero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;starting server&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we add this section to our &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  glitchtip-flask-app:
    build:
      context: flask-app
      dockerfile: Dockerfile
    container_name: glitchtip-flask-app
    restart: unless-stopped
    depends_on:
      glitchtip-traefik:
        condition: service_started
    environment:
      DSN: "https://6ee24c1a446347b99e9574edfe990393@gt.civo.rbkr.xyz/1"
    networks:
      - glitchtip
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.app.rule=Host(`flask-app.civo.rbkr.xyz`)'
      - 'traefik.http.routers.app.entrypoints=https'
      - 'traefik.http.routers.app.tls=true'
      - 'traefik.http.routers.app.tls.certresolver=letsencrypt'
      - 'traefik.http.routers.app.service=app-service'
      - 'traefik.http.services.app-service.loadbalancer.server.port=80'
    logging: *default-logging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now our full &lt;code&gt;docker-compose.yml&lt;/code&gt; should look like this more or less:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
version: '3.8'

x-logging:
  &amp;amp;default-logging
  driver: "json-file"
  options:
    max-size: "1m"

services:
  glitchtip-traefik:
    image: traefik:2.4
    container_name: glitchtip-traefik
    restart: unless-stopped
    volumes:
      - ./traefik/acme.json:/acme.json
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - glitchtip
    ports:
      - 80:80
      - 443:443
    command:
      - '--api'
      - '--providers.docker=true'
      - '--providers.docker.exposedByDefault=false'
      - '--entrypoints.http=true'
      - '--entrypoints.http.address=:80'
      - '--entrypoints.http.http.redirections.entrypoint.to=https'
      - '--entrypoints.http.http.redirections.entrypoint.scheme=https'
      - '--entrypoints.https=true'
      - '--entrypoints.https.address=:443'
      - '--certificatesResolvers.letsencrypt.acme.email=me@example.com'
      - '--certificatesResolvers.letsencrypt.acme.storage=acme.json'
      - '--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http'
      - '--log=true'
      - '--log.level=INFO'
    logging:
      driver: "json-file"
      options:
        max-size: "1m"

  glitchtip-postgres:
    image: postgres:13
    container_name: glitchtip-postgres
    restart: unless-stopped
    environment:
      POSTGRES_HOST_AUTH_METHOD: "trust"
      POSTGRES_DB: postgres
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    networks:
      - glitchtip
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 5s
      retries: 5
    logging: *default-logging

  glitchtip-redis:
    image: redis
    container_name: glitchtip-redis
    restart: unless-stopped
    networks:
      - glitchtip
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 30s
      retries: 50
    logging: *default-logging

  glitchtip-web:
    image: glitchtip/glitchtip
    container_name: glitchtip-web
    restart: unless-stopped
    depends_on:
      glitchtip-traefik:
        condition: service_started
      glitchtip-postgres:
        condition: service_healthy
      glitchtip-redis:
        condition: service_healthy
    environment:
      DATABASE_URL: postgres://postgres:postgres@glitchtip-postgres:5432/postgres
      REDIS_URL: redis://glitchtip-redis:6379/0
      SECRET_KEY: jsdf93892309fhufhr
      PORT: 8000
      EMAIL_URL: "${EMAIL_URL}"
      DEFAULT_FROM_EMAIL: ${EMAIL_FROM_ADDRESS}
      GLITCHTIP_DOMAIN: "https://gt.civo.rbkr.xyz"
      ENABLE_OPEN_USER_REGISTRATION: "False"
      GLITCHTIP_MAX_EVENT_LIFE_DAYS: 90
    networks:
      - glitchtip
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.glitchtip.rule=Host(`gt.civo.rbkr.xyz`)'
      - 'traefik.http.routers.glitchtip.entrypoints=https'
      - 'traefik.http.routers.glitchtip.tls=true'
      - 'traefik.http.routers.glitchtip.tls.certresolver=letsencrypt'
      - 'traefik.http.routers.glitchtip.service=glitchtip-service'
      - 'traefik.http.services.glitchtip-service.loadbalancer.server.port=8000'
    logging: *default-logging

  glitchtip-worker:
    image: glitchtip/glitchtip
    container_name: glitchtip-worker
    restart: unless-stopped
    command: celery -A glitchtip worker -B -l INFO
    depends_on:
      glitchtip-postgres:
        condition: service_healthy
      glitchtip-redis:
        condition: service_healthy
    environment:
      DATABASE_URL: postgres://postgres:postgres@glitchtip-postgres:5432/postgres
      REDIS_URL: redis://glitchtip-redis:6379/0
      SECRET_KEY: jsdf93892309fhufhr
      PORT: 8000
      EMAIL_URL: "${EMAIL_URL}"
      DEFAULT_FROM_EMAIL: ${EMAIL_FROM_ADDRESS}
    networks:
      - glitchtip
    logging: *default-logging

  glitchtip-migrate:
    image: glitchtip/glitchtip
    container_name: glitchtip-migrate
    depends_on:
      glitchtip-postgres:
        condition: service_healthy
      glitchtip-redis:
        condition: service_healthy
    command: "./manage.py migrate"
    environment:
      DATABASE_URL: postgres://postgres:postgres@glitchtip-postgres:5432/postgres
      REDIS_URL: redis://glitchtip-redis:6379/0
      SECRET_KEY: jsdf93892309fhufhr
      PORT: 8000
      EMAIL_URL: "${EMAIL_URL}"
      DEFAULT_FROM_EMAIL: ${EMAIL_FROM_ADDRESS}
      GLITCHTIP_DOMAIN: "https://gt.civo.rbkr.xyz"
    networks:
      - glitchtip
    logging: *default-logging

  glitchtip-flask-app:
    build:
      context: flask-app
      dockerfile: Dockerfile
    container_name: glitchtip-flask-app
    restart: unless-stopped
    depends_on:
      glitchtip-traefik:
        condition: service_started
    environment:
      DSN: "https://6ee24c1a446347b99e9574edfe990393@gt.civo.rbkr.xyz/1"
    networks:
      - glitchtip
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.app.rule=Host(`flask-app.civo.rbkr.xyz`)'
      - 'traefik.http.routers.app.entrypoints=https'
      - 'traefik.http.routers.app.tls=true'
      - 'traefik.http.routers.app.tls.certresolver=letsencrypt'
      - 'traefik.http.routers.app.service=app-service'
      - 'traefik.http.services.app-service.loadbalancer.server.port=80'
    logging: *default-logging

networks:
  glitchtip:
    name: glitchtip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build the &lt;code&gt;flask-app&lt;/code&gt; container and start the service:&lt;br&gt;
&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;--build&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making a test request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -IL http://flask-app.civo.rbkr.xyz
HTTP/1.1 308 Permanent Redirect
Location: https://flask-app.civo.rbkr.xyz/
Date: Tue, 28 Sep 2021 06:00:27 GMT
Content-Length: 18
Content-Type: text/plain; charset=utf-8

HTTP/2 200
content-type: text/html; charset=utf-8
date: Tue, 28 Sep 2021 06:00:27 GMT
server: gunicorn/20.0.4
content-length: 12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Logging Exceptions to Glitchtip
&lt;/h2&gt;

&lt;p&gt;Now let's access the endpoints that will cause an exception, let's first make a request to &lt;code&gt;/trigger-error&lt;/code&gt;, then head over to "Issues", or if you have multiple projects, select your organization, then select your project's issues:&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%2Fpaeka87h8du8sddgx026.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%2Fpaeka87h8du8sddgx026.png" alt="image" width="732" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the issues view, we can see we had one issue:&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%2Fx9mjv2q7kuxn0bn1n7yq.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%2Fx9mjv2q7kuxn0bn1n7yq.png" alt="image" width="800" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we select the issue, we can see more information about this exception, such as the useragent info, the request path, where in our application the exception was raised etc:&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%2Fcd4qpfbbubw3veuai3cv.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%2Fcd4qpfbbubw3veuai3cv.png" alt="image" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, Glitchtip also provide information deeper in the stack where the exception was raised from:&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%2F12gf6y0vuqxy1wo7v82t.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%2F12gf6y0vuqxy1wo7v82t.png" alt="image" width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a lot of information, but what I also found nice, was that it logs span_id and trace_id, the sdk version etc:&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%2Fvadz6ircrvrzmaf7bf69.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%2Fvadz6ircrvrzmaf7bf69.png" alt="image" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we have a look at the &lt;code&gt;/log-error&lt;/code&gt;, which will just log an error event, from the issues view, we can see we had one issue:&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%2F2p73mdx7ek7gajlg26r5.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%2F2p73mdx7ek7gajlg26r5.png" alt="image" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we select the issue, we can see more information about this exception, such as the useragent info, the request path, etc:&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%2Fhz3a3it0iq5va3iv7vqg.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%2Fhz3a3it0iq5va3iv7vqg.png" alt="image" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Alerting
&lt;/h2&gt;

&lt;p&gt;We can setup alerting for every time we receive an exception, to do so, head over to "Settings", "Projects" and you will find "Project Alerts":&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%2F3dzvl3fqk2ic60oisego.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%2F3dzvl3fqk2ic60oisego.png" alt="image" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we select "Create New Alert", you will get the alert configuration and by default the alert will be configured to the email address of the team members of the project. &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%2F17hbzlrj3floboxwtil7.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%2F17hbzlrj3floboxwtil7.png" alt="image" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we "Add Alert Recipient", you will see that we have the option to add a "Webhook URL", so let's go back to our &lt;code&gt;docker-compose.yml&lt;/code&gt; and setup a Python Flask Webhook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  glitchtip-flask-webhook:
    build:
      context: flask-webhook
      dockerfile: Dockerfile
    container_name: glitchtip-flask-webhook
    restart: unless-stopped
    depends_on:
      glitchtip-traefik:
        condition: service_started
    networks:
      - glitchtip
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.webhook.rule=Host(`flask-webhook.civo.rbkr.xyz`)'
      - 'traefik.http.routers.webhook.entrypoints=https'
      - 'traefik.http.routers.webhook.tls=true'
      - 'traefik.http.routers.webhook.tls.certresolver=letsencrypt'
      - 'traefik.http.routers.webhook.service=webhook-service'
      - 'traefik.http.services.webhook-service.loadbalancer.server.port=80'
    logging: *default-logging
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/health"]
      interval: 5s
      timeout: 3s
      retries: 60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we create our flask-webhook directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir flask-webhook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the required files, first our application &lt;code&gt;flask-webhook/app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DEBUG&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="nd"&gt;@app.before_first_request&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup_logging&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StreamHandler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/health&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;up&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;starting server&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then our &lt;code&gt;flask-webhook/Dockerfile&lt;/code&gt;:&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="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.8-slim&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;curl &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; requirements.txt /src/requirements.txt&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; run.sh /src/run.sh&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /src/run.sh
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /src/requirements.txt
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; app.py /src/app.py&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/src/run.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then our &lt;code&gt;flask-webhook/requirements.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flask==1.1.2
gunicorn==20.0.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then when everything is in place, build and start 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;docker-compose up --build -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the application has started, from Settings, Projects, Project Alerts, when we add a new alert recipient, we select the webhook as a recipient type and add our webhook url:&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%2F6dsb844dfva1w1qe45us.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%2F6dsb844dfva1w1qe45us.png" alt="image" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we should see:&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%2Fs4scroh726o6ffahd9oj.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%2Fs4scroh726o6ffahd9oj.png" alt="image" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select submit and then invoke the &lt;code&gt;/trigger-error&lt;/code&gt; url:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -I https://flask-app.civo.rbkr.xyz/trigger-error
HTTP/2 500
content-type: text/html; charset=utf-8
date: Tue, 28 Sep 2021 09:36:29 GMT
server: gunicorn/20.0.4
content-length: 290
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we look at the logs of our webhook container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose logs -f glitchtip-flask-webhook
...
glitchtip-flask-webhook    | {'alias': 'GlitchTip', 'text': 'GlitchTip Alert', 'attachments': [{'title': 'ZeroDivisionError: division by zero', 'title_link': 'https://gt.civo.rbkr.xyz/my-org/issues/2', 'text': 'trigger_error', 'image_url': None, 'color': '#e52b50'}], 'sections': [{'activityTitle': 'ZeroDivisionError: division by zero', 'activitySubtitle': '[View Issue FLASK-SERVICE-DEV-1](https://gt.civo.rbkr.xyz/my-org/issues/2)'}]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we prettify it, you can see we can consume this information and do our own integrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; print(json.dumps(e, indent=2))
{
  "alias": "GlitchTip",
  "text": "GlitchTip Alert",
  "attachments": [
    {
      "title": "ZeroDivisionError: division by zero",
      "title_link": "https://gt.civo.rbkr.xyz/my-org/issues/2",
      "text": "trigger_error",
      "image_url": null,
      "color": "#e52b50"
    }
  ],
  "sections": [
    {
      "activityTitle": "ZeroDivisionError: division by zero",
      "activitySubtitle": "[View Issue FLASK-SERVICE-DEV-1](https://gt.civo.rbkr.xyz/my-org/issues/2)"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our email notification will look like this:&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%2Fuser-images.githubusercontent.com%2F567298%2F135065119-be98faff-1c70-47f3-812a-893c348ffd50.png" class="article-body-image-wrapper"&gt;&lt;img alt="image" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F567298%2F135065119-be98faff-1c70-47f3-812a-893c348ffd50.png" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complete Compose
&lt;/h2&gt;

&lt;p&gt;This will be published to my &lt;a href="https://github.com/ruanbekker/docker-glitchtip-traefik" rel="noopener noreferrer"&gt;github repository&lt;/a&gt;, but the complete &lt;code&gt;docker-compose.yml&lt;/code&gt;:&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;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.8'&lt;/span&gt;

&lt;span class="na"&gt;x-logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="nl"&gt;&amp;amp;default-logging&lt;/span&gt;
  &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&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;glitchtip-traefik&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;traefik:2.4&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;glitchtip-traefik&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="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;./traefik/acme.json:/acme.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&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;glitchtip&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="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;443:443&lt;/span&gt;
    &lt;span class="na"&gt;command&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;--api'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--providers.docker=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--providers.docker.exposedByDefault=false'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.address=:80'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.http.redirections.entrypoint.to=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.http.http.redirections.entrypoint.scheme=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.https=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.https.address=:443'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.email=email@example.com'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.storage=acme.json'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--log=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--log.level=INFO'&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m"&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-postgres&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;postgres:13&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;glitchtip-postgres&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="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_HOST_AUTH_METHOD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trust"&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;PGDATA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/postgresql/data/pgdata&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;./data/postgres:/var/lib/postgresql/data&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;glitchtip&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-U"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-redis&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;redis&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;glitchtip-redis&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="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;glitchtip&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis-cli"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&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;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-web&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;glitchtip/glitchtip&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;glitchtip-web&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="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-traefik&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;service_started&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-postgres&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;service_healthy&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-redis&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;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:postgres@glitchtip-postgres:5432/postgres&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://glitchtip-redis:6379/0&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jsdf93892309fhufhr&lt;/span&gt;
      &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;
      &lt;span class="na"&gt;EMAIL_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${EMAIL_URL}"&lt;/span&gt;
      &lt;span class="na"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${EMAIL_FROM_ADDRESS}&lt;/span&gt;
      &lt;span class="na"&gt;GLITCHTIP_DOMAIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gt.civo.rbkr.xyz"&lt;/span&gt;
      &lt;span class="na"&gt;ENABLE_OPEN_USER_REGISTRATION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;False"&lt;/span&gt;
      &lt;span class="na"&gt;GLITCHTIP_MAX_EVENT_LIFE_DAYS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&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;glitchtip&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="s1"&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.rule=Host(`gt.civo.rbkr.xyz`)'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.entrypoints=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.tls=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.tls.certresolver=letsencrypt'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.glitchtip.service=glitchtip-service'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.glitchtip-service.loadbalancer.server.port=8000'&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-worker&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;glitchtip/glitchtip&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;glitchtip-worker&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="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;celery -A glitchtip worker -B -l INFO&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-postgres&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;service_healthy&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-redis&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;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:postgres@glitchtip-postgres:5432/postgres&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://glitchtip-redis:6379/0&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jsdf93892309fhufhr&lt;/span&gt;
      &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;
      &lt;span class="na"&gt;EMAIL_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${EMAIL_URL}"&lt;/span&gt;
      &lt;span class="na"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${EMAIL_FROM_ADDRESS}&lt;/span&gt;
      &lt;span class="na"&gt;GLITCHTIP_DOMAIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gt.civo.rbkr.xyz"&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;glitchtip&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-migrate&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;glitchtip/glitchtip&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;glitchtip-migrate&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-postgres&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;service_healthy&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-redis&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;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./manage.py&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;migrate"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:postgres@glitchtip-postgres:5432/postgres&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://glitchtip-redis:6379/0&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jsdf93892309fhufhr&lt;/span&gt;
      &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;
      &lt;span class="na"&gt;EMAIL_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${EMAIL_URL}"&lt;/span&gt;
      &lt;span class="na"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${EMAIL_FROM_ADDRESS}&lt;/span&gt;
      &lt;span class="na"&gt;GLITCHTIP_DOMAIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gt.civo.rbkr.xyz"&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;glitchtip&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-flask-app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flask-app&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&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;glitchtip-flask-app&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="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-traefik&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;service_started&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DSN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://6ee24c1a446347b99e9574edfe990393@gt.civo.rbkr.xyz/1"&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;glitchtip&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="s1"&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.app.rule=Host(`flask-app.civo.rbkr.xyz`)'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.app.entrypoints=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.app.tls=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.app.tls.certresolver=letsencrypt'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.app.service=app-service'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.app-service.loadbalancer.server.port=80'&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;

  &lt;span class="na"&gt;glitchtip-flask-webhook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flask-webhook&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&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;glitchtip-flask-webhook&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="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;glitchtip-traefik&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;service_started&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;glitchtip&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="s1"&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.webhook.rule=Host(`flask-webhook.civo.rbkr.xyz`)'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.webhook.entrypoints=https'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.webhook.tls=true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.webhook.tls.certresolver=letsencrypt'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.webhook.service=webhook-service'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.webhook-service.loadbalancer.server.port=80'&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-logging&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:80/health"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&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;glitchtip&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;glitchtip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I left the domain endpoints and glitchtip application keys in the code intentionally for demonstration purposes, but deleted the stack after the time of writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;I hope this was helpful, I was really impressed with Glitchtip. If you liked this content, please make sure to share or come say hi on my website or twitter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://ruan.dev" rel="noopener noreferrer"&gt;ruan.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twitter&lt;/strong&gt;: &lt;a href="https://twitter.com/ruanbekker" rel="noopener noreferrer"&gt;@ruanbekker&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>monitoring</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Interacting with your Bitcoin Node</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Mon, 13 Sep 2021 07:24:36 +0000</pubDate>
      <link>https://dev.to/ruanbekker/interacting-with-your-bitcoin-node-113c</link>
      <guid>https://dev.to/ruanbekker/interacting-with-your-bitcoin-node-113c</guid>
      <description>&lt;p&gt;This is a series of posts on running your own bitcoin full-node on the testnet chain.&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/ruanbekker/running-a-testnet-with-bitcoin-on-linux-4b0p"&gt;previous post&lt;/a&gt; I demonstrated how to setup a bitcoin full-node on the testnet chain.&lt;/p&gt;

&lt;p&gt;In this post we will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;interact with our node using the CLI and JSON-RPC&lt;/li&gt;
&lt;li&gt;create wallets and accounts &lt;/li&gt;
&lt;li&gt;send test bitcoin to our accounts&lt;/li&gt;
&lt;li&gt;track transactions using a block explorer&lt;/li&gt;
&lt;li&gt;and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pre-Requisites
&lt;/h2&gt;

&lt;p&gt;Bitcoin core should be installed and fully synced, you can reference my &lt;a href="https://dev.to/ruanbekker/running-a-testnet-with-bitcoin-on-linux-4b0p"&gt;previous post&lt;/a&gt; if you want to follow this guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  CLI Usage
&lt;/h2&gt;

&lt;p&gt;To get the current block count:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bitcoin-cli getblockcount
2091215
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get some basic info about the first block ever created on the bitcoin blockchain. As the genesis block, it has the index value 0. We can use &lt;code&gt;getblockhas&lt;/code&gt; to get the hash value for the first block ever created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bitcoin-cli getblockhash 0
000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now use &lt;code&gt;getblock&lt;/code&gt; with the hash value to retrieve details about the block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bitcoin-cli getblock 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943
{
  "hash": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
  "confirmations": 2004218,
  "strippedsize": 285,
  "size": 285,
  "weight": 1140,
  "height": 0,
  "version": 1,
  "versionHex": "00000001",
  "merkleroot": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
  "tx": [
    "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
  ],
  "time": 1296688602,
  "mediantime": 1296688602,
  "nonce": 414098458,
  "bits": "1d00ffff",
  "difficulty": 1,
  "chainwork": "0000000000000000000000000000000000000000000000000000000100010001",
  "nTx": 1,
  "nextblockhash": "00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use the &lt;code&gt;nextblockhash&lt;/code&gt; to retrieve information about block 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bitcoin-cli getblock 00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206
{
  "hash": "00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206",
  "confirmations": 2004217,
  "strippedsize": 190,
  "size": 190,
  "weight": 760,
  "height": 1,
  "version": 1,
  "versionHex": "00000001",
  "merkleroot": "f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba",
  "tx": [
    "f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba"
  ],
  "time": 1296688928,
  "mediantime": 1296688928,
  "nonce": 1924588547,
  "bits": "1d00ffff",
  "difficulty": 1,
  "chainwork": "0000000000000000000000000000000000000000000000000000000200020002",
  "nTx": 1,
  "previousblockhash": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
  "nextblockhash": "000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use &lt;code&gt;-getinfo&lt;/code&gt; to get information show as the verification progress and balances in our wallets, should they exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bitcoin-cli -getinfo
{
  "version": 210100,
  "blocks": 2091215,
  "headers": 2091215,
  "verificationprogress": 0.9999993040419591,
  "timeoffset": 0,
  "connections": {
    "in": 0,
    "out": 10,
    "total": 10
  },
  "proxy": "",
  "difficulty": 16777216,
  "chain": "test",
  "relayfee": 0.00001000,
  "warnings": "Warning: unknown new rules activated (versionbit 28)",
  "balances": {
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more examples view &lt;a href="https://chainquery.com/bitcoin-cli"&gt;chainquery&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON RPC Usage
&lt;/h2&gt;

&lt;p&gt;First get the jsonrpc user and password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat ~/.bitcoin/bitcoin.conf | grep -E '(rpcuser|rpcpassword)'
rpcuser=bitcoin
rpcpassword=xxxxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because this is a test environment, we can set the username and password as an environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# the user and password might differ on your setup
$ export bitcoinauth="bitcoin:xxxxxxxxxxxxxx"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wallet Interaction
&lt;/h3&gt;

&lt;p&gt;Create a wallet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "createwallet", "params": ["wallet"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/
{"result":{"name":"wallet","warning":""},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List wallets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "tutorial", "method": "listwallets", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/
{"result":["wallet"],"error":null,"id":"tutorial"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we inspect the &lt;code&gt;bitcoin.conf&lt;/code&gt; we will notice that we don't have the wallet loaded in our config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat ~/.bitcoin/bitcoin.conf | grep -c 'wallet='
0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But since we created the wallet, we can see the &lt;code&gt;wallet.dat&lt;/code&gt; in our data directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ find /blockchain/ -type f -name wallet.dat
/blockchain/bitcoin/data/testnet3/wallets/wallet/wallet.dat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's restart the &lt;code&gt;bitcoind&lt;/code&gt; service and see if we can still list our wallet, first restart the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl restart bitcoind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then wait a couple of seconds and list the wallets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "tutorial", "method": "listwallets", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/
{"result":[],"error":null,"id":"tutorial"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see it's not loaded, due to it not being in the config. Let's update our config &lt;code&gt;~/.bitcoin/bitcoin.conf&lt;/code&gt;, and restart the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# more wallets can be referenced by using another wallet= config
[test]
wallet=wallet
# which corresponds to datadir + walletdir
# /blockchain/bitcoin/data/testnet3/wallets/wallet/wallet.dat
# /blockchain/bitcoin/data/testnet3/wallets/wallet/db.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I restarted the &lt;code&gt;bitcoind&lt;/code&gt; service, I checked the logs with &lt;code&gt;journalctl -fu bitcoind&lt;/code&gt;, and I could see the wallet has been loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z init message: Loading wallet...
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z BerkeleyEnvironment::Open: LogDir=/blockchain/bitcoin/data/testnet3/wallets/wallet/database ErrorFile=/blockchain/bitcoin/data/testnet3/wallets/wallet//db.log
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z txindex thread start
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z txindex is enabled at height 2004267
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z txindex thread exit
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z [wallet] Wallet File Version = 169900
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z [wallet] Keys: 2001 plaintext, 0 encrypted, 2001 w/ metadata, 2001 total. Unknown wallet records: 0
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z [wallet] Wallet completed loading in              39ms
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z init message: Rescanning...
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z [wallet] Rescanning last 2 blocks (from block 2004265)...
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z [wallet] Rescan started from block 000000000000001f778a8e2b68cf05490ae000e653b925bb0552c1b79ef4fe70...
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z [wallet] Rescan completed in               2ms
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z [wallet] setKeyPool.size() = 2000
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z [wallet] mapWallet.size() = 0
Jun 10 13:16:23 ip-172-31-82-15 bitcoind[41053]: 2021-06-10T13:16:23Z [wallet] m_address_book.size() = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I list the wallets again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "tutorial", "method": "listwallets", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/
{"result":["wallet"],"error":null,"id":"tutorial"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the wallet has been loaded, we can get wallet info:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "tutorial", "method": "getwalletinfo", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/ | jq .
{
  "result": {
    "walletname": "wallet",
    "walletversion": 169900,
    "format": "bdb",
    "balance": 0,
    "unconfirmed_balance": 0,
    "immature_balance": 0,
    "txcount": 0,
    "keypoololdest": 1623329789,
    "keypoolsize": 1000,
    "hdseedid": "x",
    "keypoolsize_hd_internal": 1000,
    "paytxfee": 0,
    "private_keys_enabled": true,
    "avoid_reuse": false,
    "scanning": false,
    "descriptors": false
  },
  "error": null,
  "id": "tutorial"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create another wallet, named &lt;code&gt;test-wallet&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "createwallet", "params": ["test-wallet"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/
{"result":{"name":"test-wallet","warning":""},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we created our wallet, list the wallets again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "listwallets", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/
{"result":["wallet","test-wallet"],"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have 2 wallets, we need to specify the wallet name, when we want to do a &lt;code&gt;getwalletinfo&lt;/code&gt; method for a specific wallet, &lt;code&gt;test-wallet&lt;/code&gt; in this case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getwalletinfo", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet | jq .
{
  "result": {
    "walletname": "test-wallet",
    "walletversion": 169900,
    "format": "bdb",
    "balance": 0,
    "unconfirmed_balance": 0,
    "immature_balance": 0,
    "txcount": 0,
    "keypoololdest": 1623333495,
    "keypoolsize": 1000,
    "hdseedid": "x",
    "keypoolsize_hd_internal": 1000,
    "paytxfee": 0,
    "private_keys_enabled": true,
    "avoid_reuse": false,
    "scanning": false,
    "descriptors": false
  },
  "error": null,
  "id": "curltest"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to understand where the data resides for our &lt;code&gt;test-wallet&lt;/code&gt;, we can use &lt;code&gt;find&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ find /blockchain/ -type f -name wallet.dat | grep test-wallet
/blockchain/bitcoin/data/testnet3/wallets/test-wallet/wallet.dat

$ find /blockchain/ -type f -name db.log | grep test-wallet
/blockchain/bitcoin/data/testnet3/wallets/test-wallet/db.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include the wallet name in the config located at &lt;code&gt;~/.bitcoin/bitcoin.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[test]
wallet=wallet
wallet=test-wallet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart bitcoind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl restart bitcoind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now list our wallets again, and you should see they are being read from config and the wallets will persist if your node restarts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "listwallets", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/
{"result":["wallet","test-wallet"],"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To backup a wallet, the &lt;code&gt;test-wallet&lt;/code&gt; in this case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "backupwallet", "params": ["test-wallet_bak.dat"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":null,"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check where the file was backed up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ find /blockchain/ -name test-wallet_bak.dat
/blockchain/bitcoin/data/test-wallet_bak.dat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Addresses
&lt;/h3&gt;

&lt;p&gt;At this moment we have wallets, but we don't have any addresses associated to those wallets, we can verify this by listing wallet addresses using the &lt;code&gt;getaddressesbylabel&lt;/code&gt; and passing a empty label as new addresses gets no labels assigned by default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getaddressesbylabel", "params": [""]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":{"":{"purpose":"receive"}},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's generate a new address for our &lt;code&gt;test-wallet&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getnewaddress", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":"tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc","error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see our address for &lt;code&gt;test-wallet&lt;/code&gt; is &lt;code&gt;tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc&lt;/code&gt;, note that you can have multiple addresses per wallet.&lt;/p&gt;

&lt;p&gt;To get address information for the wallet by using the &lt;code&gt;getaddressinfo&lt;/code&gt; method and passing the wallet address as the parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getaddressinfo", "params": ["tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet | jq .
{
  "result": {
    "address": "tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc",
    "scriptPubKey": "x",
    "ismine": true,
    "solvable": true,
    "desc": "wpkh([05c34822/0'/0'/0']x)#x3x5vu3t",
    "iswatchonly": false,
    "isscript": false,
    "iswitness": true,
    "witness_version": 0,
    "witness_program": "1eb57750b62dxe284e32cd44f6e49",
    "pubkey": "023a1250c0d44751b604656x649357b5e530b9f8500f03ab5b",
    "ischange": false,
    "timestamp": 1623333494,
    "hdkeypath": "m/0'/0'/0'",
    "hdseedid": "x",
    "hdmasterfingerprint": "x",
    "labels": [
      ""
    ]
  },
  "error": null,
  "id": "curltest"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As before, we can view the address by label, to view the address for your wallet, we will now see our address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getaddressesbylabel", "params": [""]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":{"tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc":{"purpose":"receive"}},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get available wallet balance with at least 6 confirmations,:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getbalance", "params": ["*", 6]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":0.00000000,"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get balances (all balances) for the &lt;code&gt;test-wallet&lt;/code&gt; wallet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getbalances", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":{"mine":{"trusted":0.00000000,"untrusted_pending":0.00000000,"immature":0.00000000}},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getnewaddress", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":"tb1qa7e0mmgsul6pnxhzx7rw49y9qf35enqqra47hh","error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List all addresses for the wallet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# by default new addresses has no labels, therefore it returns both
$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getaddressesbylabel", "params": [""]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":{"tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc":{"purpose":"receive"},"tb1qa7e0mmgsul6pnxhzx7rw49y9qf35enqqra47hh":{"purpose":"receive"}},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Labelling Addresses
&lt;/h3&gt;

&lt;p&gt;Now we can label addresses on wallets, to label the first address as "green":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "setlabel", "params": ["tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc", "green"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":null,"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Label the new address as "blue":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "setlabel", "params": ["tb1qa7e0mmgsul6pnxhzx7rw49y9qf35enqqra47hh", "blue"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":null,"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can list addresses for our wallet by the label, "blue" in this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getaddressesbylabel", "params": ["blue"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":{"tb1qa7e0mmgsul6pnxhzx7rw49y9qf35enqqra47hh":{"purpose":"receive"}},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List addresses for our wallet by the label "green":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getaddressesbylabel", "params": ["green"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":{"tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc":{"purpose":"receive"}},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create another address for our test-walet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getnewaddress", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":"tb1qunk223dztk2j2zqswleyenwu3chfqt642vrp8z","error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the new address to the green label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "setlabel", "params": ["tb1qunk223dztk2j2zqswleyenwu3chfqt642vrp8z", "green"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":null,"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List addresses by green label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getaddressesbylabel", "params": ["green"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":{"tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc":{"purpose":"receive"},"tb1qunk223dztk2j2zqswleyenwu3chfqt642vrp8z":{"purpose":"receive"}},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Receive tBTC
&lt;/h3&gt;

&lt;p&gt;You can receive free test btc, by using any of these testnet faucet websites to receive tBTC over testnet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://testnet-faucet.mempool.co/"&gt;https://testnet-faucet.mempool.co/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://testnet.qc.to/"&gt;https://testnet.qc.to/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://onchain.io/bitcoin-testnet-faucet"&gt;https://onchain.io/bitcoin-testnet-faucet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The transaction details for sending 0.001 tbtc to my &lt;code&gt;tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc&lt;/code&gt; address, we will receive the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TxID: &lt;code&gt;637ea98aca23411059ad79aca7ea36ae30b68a173d89e6644703a06a1a846c25&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Destination Address: &lt;code&gt;tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Amount: &lt;code&gt;0.001&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then to list transactions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "listtransactions", "params": ["*"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet | jq .
{
  "result": [
    {
      "address": "tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc",
      "category": "receive",
      "amount": 0.001,
      "label": "green",
      "vout": 1,
      "confirmations": 0,
      "trusted": false,
      "txid": "637ea98aca23411059ad79aca7ea36ae30b68a173d89e6644703a06a1a846c25",
      "walletconflicts": [],
      "time": 1623337058,
      "timereceived": 1623337058,
      "bip125-replaceable": "no"
    }
  ],
  "error": null,
  "id": "curltest"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see at the time, there were 0 confirmations, we can see the same txid as well as other info. With the testnet, we require at least 1 confirmation before a transaction is confirmed, where the mainnet requires 6.&lt;/p&gt;

&lt;p&gt;To get balances for our wallet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getbalances", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":{"mine":{"trusted":0.00000000,"untrusted_pending":0.00100000,"immature":0.00000000}},"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see as we don't have any confirmations yet, so therefore the trusted value is still 0.&lt;/p&gt;

&lt;p&gt;Listing the transactions over time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "listtransactions", "params": ["*"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet | jq .
{
  "result": [
    {
      "address": "tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc",
      "category": "receive",
      "amount": 0.001,
      "label": "green",
      "vout": 1,
      "confirmations": 0,
      "trusted": false,
      "txid": "637ea98aca23411059ad79aca7ea36ae30b68a173d89e6644703a06a1a846c25",
      "walletconflicts": [],
      "time": 1623337058,
      "timereceived": 1623337058,
      "bip125-replaceable": "no"
    }
  ],
  "error": null,
  "id": "curltest"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a couple of minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "listtransactions", "params": ["*"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet | jq .
{
  "result": [
    {
      "address": "tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc",
      "category": "receive",
      "amount": 0.001,
      "label": "green",
      "vout": 1,
      "confirmations": 2,
      "blockhash": "0000000000000000ba226ad21b51fe3998180dc354ec433ad7a4c4897e04d805",
      "blockheight": 2004280,
      "blockindex": 107,
      "blocktime": 1623337883,
      "txid": "637ea98aca23411059ad79aca7ea36ae30b68a173d89e6644703a06a1a846c25",
      "walletconflicts": [],
      "time": 1623337058,
      "timereceived": 1623337058,
      "bip125-replaceable": "no"
    }
  ],
  "error": null,
  "id": "curltest"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Block Explorer
&lt;/h3&gt;

&lt;p&gt;We can also use a blockchain explorer,  head over to a testnet blockchain explorer, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blockstream.info/testnet/tx"&gt;https://blockstream.info/testnet/tx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And provide the txid, in my case it was this one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blockstream.info/testnet/tx/637ea98aca23411059ad79aca7ea36ae30b68a173d89e6644703a06a1a846c25"&gt;https://blockstream.info/testnet/tx/637ea98aca23411059ad79aca7ea36ae30b68a173d89e6644703a06a1a846c25&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This transaction was done a while ago, so the confirmations will be much more than from the output above, but you can see the confirmations, addresses involved and tbtc amount.&lt;/p&gt;

&lt;p&gt;To only get the &lt;code&gt;trusted&lt;/code&gt; balance, using the &lt;code&gt;getbalance&lt;/code&gt; method and with at least 6 confirmations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getbalance", "params": ["*", 6]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":0.00100000,"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's send another transaction, the list the transactions using the &lt;code&gt;listtransactions&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "listtransactions", "params": ["*"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet | jq .
{
  "result": [
    {
      "address": "tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc",
      "category": "receive",
      "amount": 0.001,
      "label": "green",
      "vout": 1,
      "confirmations": 4,
      "blockhash": "0000000000000000ba226ad21b51fe3998180dc354ec433ad7a4c4897e04d805",
      "blockheight": 2004280,
      "blockindex": 107,
      "blocktime": 1623337883,
      "txid": "637ea98aca23411059ad79aca7ea36ae30b68a173d89e6644703a06a1a846c25",
      "walletconflicts": [],
      "time": 1623337058,
      "timereceived": 1623337058,
      "bip125-replaceable": "no"
    },
    {
      "address": "tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc",
      "category": "receive",
      "amount": 0.0111048,
      "label": "green",
      "vout": 0,
      "confirmations": 1,
      "blockhash": "000000000000002912e2da87e6e752c38965fc21e108aab439fcdcd82ba6e37a",
      "blockheight": 2004283,
      "blockindex": 4,
      "blocktime": 1623338496,
      "txid": "3cac023b088a2ddb2d601538edfc72cd1bff1bd2e1a1531518500c5b7a52e473",
      "walletconflicts": [],
      "time": 1623338453,
      "timereceived": 1623338453,
      "bip125-replaceable": "no"
    }
  ],
  "error": null,
  "id": "curltest"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After 12 hours, we can see that we have 102 confirmations for our first transaction and 99 transactions for the second transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "listtransactions", "params": ["*"]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet | jq .
{
  "result": [
    {
      "address": "tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc",
      "category": "receive",
      "amount": 0.001,
      "label": "green",
      "vout": 1,
      "confirmations": 102,
      "blockhash": "0000000000000000ba226ad21b51fe3998180dc354ec433ad7a4c4897e04d805",
      "blockheight": 2004280,
      "blockindex": 107,
      "blocktime": 1623337883,
      "txid": "637ea98aca23411059ad79aca7ea36ae30b68a173d89e6644703a06a1a846c25",
      "walletconflicts": [],
      "time": 1623337058,
      "timereceived": 1623337058,
      "bip125-replaceable": "no"
    },
    {
      "address": "tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc",
      "category": "receive",
      "amount": 0.0111048,
      "label": "green",
      "vout": 0,
      "confirmations": 99,
      "blockhash": "000000000000002912e2da87e6e752c38965fc21e108aab439fcdcd82ba6e37a",
      "blockheight": 2004283,
      "blockindex": 4,
      "blocktime": 1623338496,
      "txid": "3cac023b088a2ddb2d601538edfc72cd1bff1bd2e1a1531518500c5b7a52e473",
      "walletconflicts": [],
      "time": 1623338453,
      "timereceived": 1623338453,
      "bip125-replaceable": "no"
    },
    {
      "address": "tb1qr66hw59k958xrz008n679p8r9n2y7mjfr3tsjc",
      "category": "receive",
      "amount": 0.03521065,
      "label": "green",
      "vout": 5,
      "confirmations": 27,
      "blockhash": "000000000000004255a9d5af67b4649ff3f4d6a2f0c334261ca822cd9fbd00a9",
      "blockheight": 2004355,
      "blockindex": 43,
      "blocktime": 1623383177,
      "txid": "eb43868bd2c5abd97d4f5f11450952837bc3edc149478248e9453fdfb05c5187",
      "walletconflicts": [],
      "time": 1623382990,
      "timereceived": 1623382990,
      "bip125-replaceable": "no"
    }
  ],
  "error": null,
  "id": "curltest"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After 3 transactions, view the balance in the test wallet using the &lt;code&gt;getbalance&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curltest", "method": "getbalance", "params": ["*", 6]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":0.04731545,"error":null,"id":"curltest"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sending a raw transaction
&lt;/h3&gt;

&lt;p&gt;A easier way to send a transaction is by using &lt;code&gt;sendtoaddress&lt;/code&gt; and the source wallet will be in the request url, ie: &lt;code&gt;/wallet/wallet-name&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First we look if we have a address for our account that we are sending from, if not then we can create a wallet and a address, for this example, I have a address for the &lt;code&gt;wallet&lt;/code&gt; wallet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curl", "method": "getaddressesbylabel", "params": [""]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/wallet
{"result":{"tb1qks4tyrz52vvdh35kcx0ypvnj3fjdkl692pzfyc":{"purpose":"receive"}},"error":null,"id":"curl"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure that our source wallet has funds in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curl", "method": "getbalance", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/wallet | python -m json.tool
{
    "error": null,
    "id": "curl",
    "result": 0.01811929
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have enough funds to send, so we now have the source wallet name and we need to get the wallet address, where we want to send the funds to, which in this case is the address in &lt;code&gt;test-wallet&lt;/code&gt; as the destination:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curl", "method": "getaddressesbylabel", "params": [""]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet
{"result":{"tb1qzxmefmcpq98z42v67a80gvug2fe979r5h768yv":{"purpose":"receive"}},"error":null,"id":"curl"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just to double check our current funds in the wallet that will receive funds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curl", "method": "getbalance", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/test-wallet | python -m json.tool
{
    "error": null,
    "id": "curl",
    "result": 0.35572584
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we will use the &lt;code&gt;sendtoaddress&lt;/code&gt; method, with the recipient address and the amount to send as the parameters. To summarize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source Wallet: &lt;code&gt;wallet&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Destination Address:  &lt;code&gt;tb1qzxmefmcpq98z42v67a80gvug2fe979r5h768yv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Amount to send: &lt;code&gt;0.01.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sending the amount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth"  -d '{"jsonrpc": "1.0", "id":"0", "method": "sendtoaddress", "params":["tb1qzxmefmcpq98z42v67a80gvug2fe979r5h768yv", 0.01]}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/wallet
{"result":"df087095ac79d678f9d98c8bf8ebface2ac62a20546d85e07a852feb2c3bea50","error":null,"id":"0"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will receive a transaction id, and if we list for transactions for our source wallet, we will see the transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curl", "method": "listtransactions", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/wallet | python -m json.tool
{
    "error": null,
    "id": "curl",
    "result": [
        {
            "address": "tb1qks4tyrz52vvdh35kcx0ypvnj3fjdkl692pzfyc",
            "amount": 0.01811929,
            "bip125-replaceable": "no",
            "blockhash": "000000000000003c0ac4978ba815ff8f0d7f55da98923c686118c75461fb579e",
            "blockheight": 2091275,
            "blockindex": 1,
            "blocktime": 1630677226,
            "category": "receive",
            "confirmations": 1,
            "label": "",
            "time": 1630677177,
            "timereceived": 1630677177,
            "txid": "e44b45a309284e8044a15dca8c0a895a5c7072741882281038fb185cc0c1a0d9",
            "vout": 0,
            "walletconflicts": []
        },
        {
            "abandoned": false,
            "address": "tb1qzxmefmcpq98z42v67a80gvug2fe979r5h768yv",
            "amount": -0.01,
            "bip125-replaceable": "no",
            "category": "send",
            "confirmations": 0,
            "fee": -1.41e-06,
            "time": 1630677743,
            "timereceived": 1630677743,
            "trusted": true,
            "txid": "df087095ac79d678f9d98c8bf8ebface2ac62a20546d85e07a852feb2c3bea50",
            "vout": 0,
            "walletconflicts": []
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when we look at the sender wallet, we will see the funds was deducted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curl", "method": "getbalance", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/wallet | python -m json.tool
{
    "error": null,
    "id": "curl",
    "result": 0.00811788
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when we look at the receiver wallet, we can see that the account was received:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s -u "$bitcoinauth" -d '{"jsonrpc": "1.0", "id": "curl", "method": "getbalance", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:18332/wallet/rpi01-main | python -m json.tool
{
    "error": null,
    "id": "curl",
    "result": 0.36572584
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;Thanks for reading, if you like my content, check out my &lt;strong&gt;&lt;a href="https://ruan.dev"&gt;website&lt;/a&gt;&lt;/strong&gt; or follow me at &lt;strong&gt;&lt;a href="https://twitter.com/ruanbekker"&gt;@ruanbekker&lt;/a&gt;&lt;/strong&gt; on Twitter.&lt;/p&gt;

&lt;p&gt;The source code of this blog post will be added to this Github Repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ruanbekker/bitcoin-full-node-demo"&gt;https://github.com/ruanbekker/bitcoin-full-node-demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blockchain</category>
      <category>linux</category>
      <category>bitcoin</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Running a Testnet with Bitcoin on Linux</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Mon, 13 Sep 2021 07:14:02 +0000</pubDate>
      <link>https://dev.to/ruanbekker/running-a-testnet-with-bitcoin-on-linux-4b0p</link>
      <guid>https://dev.to/ruanbekker/running-a-testnet-with-bitcoin-on-linux-4b0p</guid>
      <description>&lt;p&gt;This will be a series of posts on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The basics of Blockchain (this post)&lt;/li&gt;
&lt;li&gt;Setting up a Bitcoin Full-Node on Linux (this post)&lt;/li&gt;
&lt;li&gt;CLI and JSON RPC examples to interact with the blockchain and your wallet (&lt;a href="https://dev.to/ruanbekker/interacting-with-your-bitcoin-node-113c"&gt;post 2&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this in-depth tutorial, we will cover the &lt;strong&gt;basics of blockchain&lt;/strong&gt;, why you would want a &lt;strong&gt;bitcoin full-node&lt;/strong&gt;, how to setup a bitcoin full-node on linux using the &lt;strong&gt;testnet chain&lt;/strong&gt; and how to interact with your &lt;strong&gt;node and the blockchain&lt;/strong&gt; using the cli and the json rpc, where we will create wallets and addresses and sending tbtc to your accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blockchain Basics
&lt;/h2&gt;

&lt;p&gt;Before we start setting up our bitcoin full-node, we first need to get through some blockchain basics, if you already aware of it, you can skip the the setup section of this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Block
&lt;/h3&gt;

&lt;p&gt;Transaction data is &lt;strong&gt;permanently recorded&lt;/strong&gt; into files called &lt;strong&gt;blocks&lt;/strong&gt;. You can think of it as a &lt;strong&gt;transaction ledger&lt;/strong&gt;. Blocks are organised into a linear sequence over time.&lt;/p&gt;

&lt;p&gt;New transactions are &lt;strong&gt;constantly being processed&lt;/strong&gt; by &lt;strong&gt;miners&lt;/strong&gt; into new blocks which are added to the &lt;strong&gt;end of the chain&lt;/strong&gt;. As blocks are buried deeper and deeper into the blockchain they &lt;strong&gt;become harder&lt;/strong&gt; and harder to change or remove, this gives rise of &lt;strong&gt;&lt;a href="https://en.bitcoin.it/wiki/Irreversible_Transactions"&gt;Bitcoin's Irreversible Transactions&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The first block added to the blockchain is referred to as the &lt;strong&gt;&lt;a href="https://en.bitcoin.it/wiki/Genesis_block"&gt;genesis block&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Blockchain
&lt;/h3&gt;

&lt;p&gt;A blockchain is a transaction database shared by &lt;strong&gt;all nodes participating&lt;/strong&gt; in a system based on the bitcoin protocol. A &lt;strong&gt;full copy&lt;/strong&gt; of a currency's blockchain contains &lt;strong&gt;every&lt;/strong&gt; transaction ever executed in the currency. With this information, one can find out how much value belonged to each address at any point in history.&lt;/p&gt;

&lt;p&gt;Every block contains a hash of the previous block. This has the effect of creating a chain of blocks from the genesis block to the current block. &lt;strong&gt;Each block&lt;/strong&gt; is guaranteed to come after the &lt;strong&gt;previous block&lt;/strong&gt; chronologically because the previous block's hash would otherwise not be known. Each block is also computationally impractical to modify once it has been in the chain for a while because every block after it would also have to be regenerated. &lt;strong&gt;These properties are what make bitcoins transactions irreversible&lt;/strong&gt;. The blockchain is the main innovation of Bitcoin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mining
&lt;/h3&gt;

&lt;p&gt;Mining is the &lt;strong&gt;process&lt;/strong&gt; of &lt;strong&gt;adding transaction records&lt;/strong&gt; to bitcoin's public ledger of past transactions. The term "mining rig" is referred to where as a single computer system that performs the necessary computations for "mining".&lt;/p&gt;

&lt;p&gt;The blockchain serves to confirm transactions to the rest of the network as having taken place. Bitcoin nodes use the blockchain to &lt;strong&gt;distinguish legitimate Bitcoin transactions&lt;/strong&gt; from attempts to re-spend coins that have already been spent elsewhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node
&lt;/h3&gt;

&lt;p&gt;Any &lt;strong&gt;computer&lt;/strong&gt; that connects to the &lt;strong&gt;bitcoin network&lt;/strong&gt; is called a &lt;strong&gt;node&lt;/strong&gt;. Nodes that fully verify all of the rules of bitcoin are called full nodes. The most popular software implementation of full nodes is called bitcoin-core, its releases can be found on their &lt;strong&gt;&lt;a href="https://github.com/bitcoin/bitcoin/releases"&gt;github page&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Full Node
&lt;/h3&gt;

&lt;p&gt;A full node is a node (computer system with bitcoin-core running on it) which &lt;strong&gt;downloads every block and transaction&lt;/strong&gt; and check them against &lt;strong&gt;bitcoin's consensus rules&lt;/strong&gt;. which fully validates transactions and blocks. Almost all full nodes also help the network by accepting transactions and blocks from other full nodes, validating those transactions and blocks, and then relaying them to further full nodes.&lt;/p&gt;

&lt;p&gt;Some &lt;strong&gt;examples&lt;/strong&gt; of &lt;strong&gt;consensus rules&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocks may only &lt;a href="https://en.bitcoin.it/wiki/Controlled_supply"&gt;create&lt;/a&gt; a certain number of bitcoins. (Currently 6.25 BTC per block.)&lt;/li&gt;
&lt;li&gt;Transactions must have correct signatures for the bitcoins being spent.&lt;/li&gt;
&lt;li&gt;Transactions/blocks must be in the correct data format.&lt;/li&gt;
&lt;li&gt;Within a single &lt;a href="https://en.bitcoin.it/wiki/Block_chain"&gt;blockchain&lt;/a&gt;, a transaction output cannot be double-spent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At minimum, a full node must download every transaction that has ever taken place, all new transactions, and all block headers. Additionally, full nodes must store information about every unspent transaction output until it is spent.&lt;/p&gt;

&lt;p&gt;By default full nodes are inefficient in that they download each new transaction at least twice, and they store the entire block chain (more than 165 GB as of 20180214) forever, even though only the unspent transaction outputs (&amp;lt;2 GB) are required. Performance can improved by enabling &lt;a href="https://bitcointalk.org/index.php?topic=1377345.0"&gt;-blocksonly&lt;/a&gt; mode and enabling &lt;a href="https://bitcoin.org/en/release/v0.12.0#wallet-pruning"&gt;pruning&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Archival Nodes
&lt;/h3&gt;

&lt;p&gt;A subset of full nodes also &lt;strong&gt;accept incoming connections&lt;/strong&gt; and &lt;strong&gt;upload old blocks&lt;/strong&gt; to other peers on the network. This happens if the software is run with -listen=1 as is default.&lt;/p&gt;

&lt;p&gt;Contrary to some popular misconceptions, being an archival node is not necessary to being a full node. If a user's bandwidth is constrained then they can use -listen=0, if their disk space is constrained they can use pruning, all the while still being a fully-validating node that enforces bitcoin's consensus rules and contributing to bitcoin's overall security.&lt;/p&gt;

&lt;p&gt;Most information was referenced from &lt;strong&gt;&lt;a href="https://en.bitcoin.it/wiki/Full_node"&gt;this&lt;/a&gt;&lt;/strong&gt; wiki.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Bitcoin Core
&lt;/h2&gt;

&lt;p&gt;Now for the fun part, to setup bitcoin-core on linux, in my case I am using a fresh &lt;strong&gt;Ubuntu 20.04&lt;/strong&gt; instance and the following commands will be executed as a non-root user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;

&lt;p&gt;Set the following environment variables, the latest version for &lt;code&gt;BITCOIN_VERSION&lt;/code&gt; can be retrieved at: &lt;a href="https://bitcoincore.org/bin/"&gt;https://bitcoincore.org/bin/&lt;/a&gt;, execute the following:&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;export &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x86_64
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.21.1
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://bitcoincore.org/bin/bitcoin-core-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/bitcoin-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-linux-gnu&lt;/span&gt;.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_DATA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/blockchain/bitcoin/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Create the user and group for bitcoin:&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;sudo &lt;/span&gt;groupadd &lt;span class="nt"&gt;-r&lt;/span&gt; bitcoin
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; bitcoin &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash bitcoin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the package manager and install the dependencies:&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;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ca-certificates gnupg gpg wget jq &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download bitcoin-core and verify that the package matches the sha hash:&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; /tmp
&lt;span class="nv"&gt;$ &lt;/span&gt;wget https://bitcoincore.org/bin/bitcoin-core-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/SHA256SUMS.asc
&lt;span class="nv"&gt;$ &lt;/span&gt;wget &lt;span class="nt"&gt;-qO&lt;/span&gt; bitcoin-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-linux-gnu&lt;/span&gt;.tar.gz &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;SHA256SUMS.asc | &lt;span class="nb"&gt;grep &lt;/span&gt;bitcoin-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-linux-gnu&lt;/span&gt;.tar.gz | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $1 }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extract the package to &lt;code&gt;/opt/bitcoin/${BITCOIN_VERSION}&lt;/code&gt; and exclude any graphical user interfacing binaries, create the home directory and set the ownership:&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;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/bitcoin/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_DATA_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo tar&lt;/span&gt; &lt;span class="nt"&gt;-xzvf&lt;/span&gt; bitcoin-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-linux-gnu&lt;/span&gt;.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; /opt/bitcoin/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--strip-components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nt"&gt;-qt&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /opt/bitcoin/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; /opt/bitcoin/current
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; bitcoin:bitcoin &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_DATA_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you have to upgrade the software version of bitcoin-core in the future you can remove the symlink with &lt;code&gt;sudo rm -rf /usr/local/bitcoin/current&lt;/code&gt; and symlink to the newer version as shown above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;Create the bitcoin configuration, here you see I am using the testnet chain and due to storage restrictions for my use-case I am setting &lt;code&gt;pruning&lt;/code&gt; mode to 1GB, and if you don't set &lt;code&gt;BITCOIN_RPC_USER&lt;/code&gt; it will use the user &lt;code&gt;bitcoin&lt;/code&gt; and if you don't set &lt;code&gt;BITCOIN_RPC_PASSWORD&lt;/code&gt; it will generate a password for the json-rpc interface:&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; bitcoin.conf.tmp &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
datadir=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_DATA_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
printtoconsole=1
rpcallowip=127.0.0.1
rpcuser=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_RPC_USER&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;bitcoin&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
rpcpassword=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_RPC_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 24&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
testnet=1
prune=1000
[test]
rpcbind=127.0.0.1
rpcport=18332
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the systemd unit-file for bitcoind:&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; bitcoind.service &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;  &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
[Unit]
Description=Bitcoin Core Testnet
After=network.target

[Service]
User=bitcoin
Group=bitcoin
WorkingDirectory=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BITCOIN_DATA_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;

Type=simple
ExecStart=/usr/local/bitcoin/current/bin/bitcoind -conf=&lt;/span&gt;&lt;span class="nv"&gt;$BITCOIN_DATA_DIR&lt;/span&gt;&lt;span class="sh"&gt;/bitcoin.conf

[Install]
WantedBy=multi-user.target
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now move the temporary config files, change the ownership and symlink the bitcoin home directory to the path that we created earlier:&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;sudo mv &lt;/span&gt;bitcoin.conf.tmp &lt;span class="nv"&gt;$BITCOIN_DATA_DIR&lt;/span&gt;/bitcoin.conf
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;bitcoin:bitcoin &lt;span class="nv"&gt;$BITCOIN_DATA_DIR&lt;/span&gt;/bitcoin.conf
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; bitcoin &lt;span class="nv"&gt;$BITCOIN_DATA_DIR&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-sfn&lt;/span&gt; &lt;span class="nv"&gt;$BITCOIN_DATA_DIR&lt;/span&gt; /home/bitcoin/.bitcoin
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; bitcoin:bitcoin /home/bitcoin
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; bitcoin:bitcoin /home/bitcoin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move the systemd unit file in place, then reload systemd and start bitcoind:&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;sudo mv &lt;/span&gt;bitcoind.service /etc/systemd/system/bitcoind.service
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;bitcoind
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start bitcoind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switch to the &lt;code&gt;bitcoin&lt;/code&gt; user:&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;sudo &lt;/span&gt;su - bitcoin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And append the bitcoin path to the &lt;code&gt;~/.profile&lt;/code&gt; file so that your user know where to find the bitcoin binaries:&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;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/opt/bitcoin/current/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initial Block Download
&lt;/h2&gt;

&lt;p&gt;Once bitcoind process has started, the initial block download will start and you can get the progress as the bitcoin user using the cli:&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;bitcoin-cli &lt;span class="nt"&gt;-getinfo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or as the root user you can view the progress using &lt;code&gt;journalctl&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;&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-fu&lt;/span&gt; bitcoind
Sep 03 08:44:23 rpi-01 bitcoind[532]: 2021-09-03T06:44:23Z UpdateTip: new &lt;span class="nv"&gt;best&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2091205 &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x20c00000 &lt;span class="nv"&gt;log2_work&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;74.461678 &lt;span class="nv"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60938712 &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'2021-09-03T06:44:13Z'&lt;/span&gt; &lt;span class="nv"&gt;progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.000000 &lt;span class="nv"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.7MiB&lt;span class="o"&gt;(&lt;/span&gt;13673txo&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;If you run into any issues you can see the status of bitcoind using:&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;sudo &lt;/span&gt;systemctl status bitcoind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or check the logs:&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;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-fu&lt;/span&gt; bitcoind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interacting with the Blockchain
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/ruanbekker/interacting-with-your-bitcoin-node-113c"&gt;next post&lt;/a&gt; I will demonstrate how to create a wallet, send some test bitcoin to your wallet and how to track transactions on a public block explorer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;Thanks for reading, if you like my content, check out my &lt;strong&gt;&lt;a href="https://ruan.dev"&gt;website&lt;/a&gt;&lt;/strong&gt; or follow me at &lt;strong&gt;&lt;a href="https://twitter.com/ruanbekker"&gt;@ruanbekker&lt;/a&gt;&lt;/strong&gt; on Twitter.&lt;/p&gt;

&lt;p&gt;The source code of this blog post will be added to this Github Repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ruanbekker/bitcoin-full-node-demo"&gt;https://github.com/ruanbekker/bitcoin-full-node-demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blockchain</category>
      <category>linux</category>
      <category>tutorial</category>
      <category>bitcoin</category>
    </item>
    <item>
      <title>Why you should use Multi Stage Docker Builds</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Sat, 07 Aug 2021 01:43:37 +0000</pubDate>
      <link>https://dev.to/ruanbekker/why-you-should-use-multi-stage-docker-builds-571e</link>
      <guid>https://dev.to/ruanbekker/why-you-should-use-multi-stage-docker-builds-571e</guid>
      <description>&lt;p&gt;In this tutorial I will demonstrate how to build slim docker images using multistage docker builds, where you can save up to 800MB of disk space per image.&lt;/p&gt;

&lt;p&gt;Originally posted on &lt;a href="https://containers.fan/posts/why-you-should-use-multistage-builds/"&gt;containers.fan&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About
&lt;/h2&gt;

&lt;p&gt;We will use multistage docker builds from a alpine image as our build image to get the dependencies, build the go binary and use our scratch image to place the built binary onto our target image to have small docker images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does size matter
&lt;/h2&gt;

&lt;p&gt;So let's assume you have a orchestrator such as ECS, Swarm or Kubernetes with 100 nodes behind a Auto Scaling Group, where your cluster node count is 10 when theres low traffic, and 100 nodes when theres lots of traffic.&lt;/p&gt;

&lt;p&gt;As we scale out our service might scale from 10 replicas to 500 replicas and let's assume our container image is 800MB in size, when a new node joins a cluster the container image won't be in the cache so the docker daemon needs to download that image from the docker registry. So let's say 100 nodes join the cluster and our service scales to 100 replicas, it means that each node needs to download 800MB from the docker registry, that is about 80GB of incoming network throughput to the cluster.&lt;/p&gt;

&lt;p&gt;So when we use multistage builds and in this case using Go, we can slim down our container image to less than 3MB, if we do the same calculation, that is just less than 300MB of throughput and if a fresh node joins a cluster and the container image is not present, it will take about a second or two to download and getting the container to run (depending on internet speed) and you obviously save disk space.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go Application
&lt;/h2&gt;

&lt;p&gt;We will use a library that generates random data from &lt;a href="https://github.com/Pallinder/go-randomdata"&gt;go-randomdata&lt;/a&gt; in our application, &lt;code&gt;app.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/Pallinder/go-randomdata"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;randomdata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenerateProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randomdata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Male&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;randomdata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Female&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;randomdata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RandomGender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The new profile's username is: %s and password (md5): %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Login&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Login&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Md5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Single Stage Docker Build
&lt;/h2&gt;

&lt;p&gt;In this example we will use the golang image to get the dependencies and build the application in one image, our &lt;code&gt;Dockerfile.single_stage&lt;/code&gt;:&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="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:latest&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;builder&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /go/src/github.com/ruanbekker
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /go/src/github.com/ruanbekker&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-u&lt;/span&gt; 10001 app
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GO111MODULE=auto&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go get
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux go build &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-installsuffix&lt;/span&gt; cgo &lt;span class="nt"&gt;-o&lt;/span&gt; main .
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/go/src/github.com/ruanbekker/main"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Building the image:&lt;br&gt;
&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;-f&lt;/span&gt; Dockerfile.single_stage &lt;span class="nt"&gt;-t&lt;/span&gt; goapp:singlestage &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multi Stage Docker Build
&lt;/h2&gt;

&lt;p&gt;Our multi stage consist of a build image where we will use the golang image to fetch our dependencies and build our application, then we use the scratch image as the target to copy the compiled binary to and run the container from the slim image, our &lt;code&gt;Dockerfile.multi_stage&lt;/code&gt;:&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="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:latest&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;builder&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /go/src/github.com/ruanbekker
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /go/src/github.com/ruanbekker&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-u&lt;/span&gt; 10001 app
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GO111MODULE=auto&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go get
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux go build &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-installsuffix&lt;/span&gt; cgo &lt;span class="nt"&gt;-o&lt;/span&gt; main .

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; scratch&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /go/src/github.com/ruanbekker/main /main&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /etc/passwd /etc/passwd&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/main"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Building the image:&lt;br&gt;
&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;-f&lt;/span&gt; Dockerfile.multi_stage &lt;span class="nt"&gt;-t&lt;/span&gt; goapp:multistage &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Comparing the size differences
&lt;/h2&gt;

&lt;p&gt;When we compare the size differences of our docker images between a normal build and a multi-stage build we can see a huge difference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-3&lt;/span&gt;
REPOSITORY                        TAG                 IMAGE ID            CREATED             SIZE
goapp                             singlestage         0d0c1f4c98a2        54 seconds ago      896MB
goapp                             multistage          d74ac27a39c8        2 hours ago         2.75MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just to show that both containers run from the built docker images, our single build:&lt;br&gt;
&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;-it&lt;/span&gt; goapp:singlestage
The new profile&lt;span class="s1"&gt;'s username is: Maregrass and password (md5): 56da7705b7648a38f539b043e6a494be
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our multi-stage build:&lt;br&gt;
&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;-it&lt;/span&gt; goapp:multistage
The new profile&lt;span class="s1"&gt;'s username is: Shirtplaid and password (md5): 7d8606ee86f2da3ed12c595ab617bf4e
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;If you liked this content, please make sure to share or come say hi on my website or twitter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://ruan.dev"&gt;ruan.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twitter&lt;/strong&gt;: &lt;a href="https://twitter.com/ruanbekker"&gt;@ruanbekker&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>java</category>
      <category>maven</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Basic Logging with Python</title>
      <dc:creator>Ruan Bekker</dc:creator>
      <pubDate>Sat, 07 Aug 2021 01:38:47 +0000</pubDate>
      <link>https://dev.to/ruanbekker/basic-logging-with-python-4cde</link>
      <guid>https://dev.to/ruanbekker/basic-logging-with-python-4cde</guid>
      <description>&lt;p&gt;I'm trying to force myself to move away from using the &lt;code&gt;print()&lt;/code&gt; function as I'm pretty much using print all the time to cater for logging, and using the &lt;code&gt;logging&lt;/code&gt; package instead.&lt;/p&gt;

&lt;p&gt;This is a basic example of using logging in a basic python app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"%(asctime)s [%(levelname)s] %(name)s %(message)s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StreamHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;messagestring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'info'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'info message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'warn'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'this is a warning'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'err'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'this is a error'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'thisapp'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'message: {}'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messagestring&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'info'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'message: {}'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messagestring&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'warn'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'message: {}'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messagestring&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'err'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When running this example, this is the output that you will see:&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;python app.py
2021-07-19 13:07:43,647 &lt;span class="o"&gt;[&lt;/span&gt;INFO] thisapp message: info message
2021-07-19 13:07:43,647 &lt;span class="o"&gt;[&lt;/span&gt;WARNING] thisapp message: this is a warning
2021-07-19 13:07:43,647 &lt;span class="o"&gt;[&lt;/span&gt;ERROR] thisapp message: this is a error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More more info on this package, see it's documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/logging.html"&gt;https://docs.python.org/3/library/logging.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading, if you like my content, check out my &lt;strong&gt;&lt;a href="https://ruan.dev"&gt;website&lt;/a&gt;&lt;/strong&gt; or follow me at &lt;strong&gt;&lt;a href="https://twitter.com/ruanbekker"&gt;@ruanbekker&lt;/a&gt;&lt;/strong&gt; on Twitter.&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>logging</category>
    </item>
  </channel>
</rss>
