<?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: csgeek</title>
    <description>The latest articles on DEV Community by csgeek (@csgeek).</description>
    <link>https://dev.to/csgeek</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%2F456799%2Faa5ee8e0-b965-43c3-90ba-8f1ea1c04c40.png</url>
      <title>DEV Community: csgeek</title>
      <link>https://dev.to/csgeek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/csgeek"/>
    <language>en</language>
    <item>
      <title>Writing Updates</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Wed, 26 Jan 2022 15:19:35 +0000</pubDate>
      <link>https://dev.to/csgeek/writing-updates-38a8</link>
      <guid>https://dev.to/csgeek/writing-updates-38a8</guid>
      <description>&lt;p&gt;I've been going back and forth on this with some friends and a few blogs that I write for a community.  &lt;/p&gt;

&lt;p&gt;When I learned how to write in school I learned a few grammar rules which are no longer valid.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always put two spaces after a period.&lt;/li&gt;
&lt;li&gt;List of items has no comma for the conjunction. aka item1, item2 and item3.  Vs item1, item2, and item3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are minor grammatical rules that are so ingrained in my muscle memory that I can't shake them.  Do you find that you have some habits that need updating since you started writing? Do they matter?  Even when I was in school I think there were some updates to proper grammar rules in the 4 years I was in High School.  Do you find any grammar rules or habits so objectionable that it'll make you close the tab? &lt;/p&gt;

&lt;p&gt;What are your thoughts? #ponderingPenguin&lt;/p&gt;

</description>
      <category>writing</category>
    </item>
    <item>
      <title>Kubernetes SSL Letsencrypt</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Fri, 04 Jun 2021 16:49:58 +0000</pubDate>
      <link>https://dev.to/csgeek/kubernetes-ssl-letsencrypt-bmf</link>
      <guid>https://dev.to/csgeek/kubernetes-ssl-letsencrypt-bmf</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Create An Application&lt;/li&gt;
&lt;li&gt;Load Balancer&lt;/li&gt;
&lt;li&gt;
Certificate Manager

&lt;ul&gt;
&lt;li&gt;Certificate Issuer&lt;/li&gt;
&lt;li&gt;Certificate&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Update Nginx To Enable SSL&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have some familiarity with Docker and Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; : Any yaml file that’s included, please apply it via kubectl apply -f file.yml.&lt;/p&gt;

&lt;p&gt;Once you have an application up and running you’ll need to ensure it’s secure. We’ll be explore using &lt;a href="https://letsencrypt.org/" rel="noopener noreferrer"&gt;letsencrypt&lt;/a&gt; to enable SSL. It’s a free certificate authority and makes it very easy to obtain a certificate. Naturally SSL doesn’t mean your app is secure, but it’s a great first step.&lt;/p&gt;

&lt;p&gt;LetsEncrypt has two methods to validate the authenticity of the request in order issue a certificate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP Validation&lt;/li&gt;
&lt;li&gt;DNS Validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we’re running in K8s, if you do use DNS validation you’ll need to use a DNS provider that integrates with K8s. In order to make this guide more portable and to avoid tying us to any particular DNS provider, I’m going to use HTTP Validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create An Application
&lt;/h2&gt;

&lt;p&gt;Before we get started, we just need to create a simple application to test this with. We’ll install a hello world app running 3 replicas and a service load balancer for port 80. Here’s the yaml I’ve used below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: hello-world
  name: hello-world
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello-world
  strategy: {}
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
        - image: docker.io/lsizani/hello
          imagePullPolicy: Always
          name: hello-world
          ports:
            - containerPort: 80
          resources: {}
      restartPolicy: Always
status: {}
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: hello-world
  name: hello-docker-svc

  namespace: default

spec:
  ports:
    - port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: hello-world
  sessionAffinity: None
  type: ClusterIP

status:
  loadBalancer: {}

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

&lt;/div&gt;



&lt;p&gt;Now, the IP address of the service is still internal so you won’t be able to connect to it unless you run the following command temporarily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl port-forward svc/hello-docker-svc 8000:80

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

&lt;/div&gt;



&lt;p&gt;After which you can connect to &lt;a href="http://127.0.0.1:8000" rel="noopener noreferrer"&gt;http://127.0.0.1:8000&lt;/a&gt; and you’ll see something along these lines:&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%2Fwww.esamir.com%2Fimages%2Fkubernetes-ssl-letsencrypt%2Fscreen1.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%2Fwww.esamir.com%2Fimages%2Fkubernetes-ssl-letsencrypt%2Fscreen1.png" alt="ScreenShot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Load Balancer
&lt;/h2&gt;

&lt;p&gt;At this point we need to actually expose this to the public. You’ll need to install an nginx-ingress Load balancer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install nginx ingress-nginx/ingress-nginx --set controller.publishService.enabled=true 

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

&lt;/div&gt;



&lt;p&gt;Configure nginx to serve traffic on port 80. Please note the lack of SSL. We’ll revisit this configuration and add SSL in a later pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-docker
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: "false"

spec:
  rules:
    - host: hello.demo.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hello-docker-svc
                port:
                  number: 80

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

&lt;/div&gt;



&lt;p&gt;I’m using the DNS name hello.demo.com which is just an example, but if your K8s cluster is configured correctly you’ll get provisioned an IP address. Please update the config to use your own DNS value and have it match the IP address.&lt;/p&gt;

&lt;p&gt;For example, if hello.demo.com doesn’t resolve to your external IP address this won’t work and it’ll fail to generate an SSL.&lt;/p&gt;

&lt;p&gt;At this point if we navigate to &lt;a href="http://hello.demo.com" rel="noopener noreferrer"&gt;http://hello.demo.com&lt;/a&gt; we’ll see the nice little hello world screen we’ve seen previously.&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%2Fwww.esamir.com%2Fimages%2Fkubernetes-ssl-letsencrypt%2Fscreen1.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%2Fwww.esamir.com%2Fimages%2Fkubernetes-ssl-letsencrypt%2Fscreen1.png" alt="ScreenShot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Certificate Manager
&lt;/h2&gt;

&lt;p&gt;Now, we need to add a certificate manager. This is where you could choose to use DNS validation. As I mentioned in the intro, I won’t be using DNS validation to make this guide agnostic to the platform. If you do simply specify the correct options you install the helm chart.&lt;/p&gt;

&lt;p&gt;Installing the cert-manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.3.1 \
  --set installCRDs=true

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Certificate Issuer
&lt;/h3&gt;

&lt;p&gt;There’s a Prod and Staging environment for LetsEncrypt. I installed both of them for convenience. Staging isn’t rate limited and allows you to debug/validate behavior without risking getting hit by a quota.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Staging&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    # You must replace this email address with your own.
    # Let's Encrypt will use this to contact you about expiring
    # certificates, and issues related to your account.
    email: user@email.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # Secret resource that will be used to store the account's private key.
      name: letsencrypt-staging-ssl
    # Add a single challenge solver, HTTP01 using nginx
    solvers:
      - http01:
          ingress:
            class: nginx

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Production&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    # You must replace this email address with your own.
    # Let's Encrypt will use this to contact you about expiring
    # certificates, and issues related to your account.
    email: samir@es.net
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # Secret resource that will be used to store the account's private key.
      name: letsencrypt-prod
    # Add a single challenge solver, HTTP01 using nginx
    solvers:
      - http01:
          ingress:
            class: nginx

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

&lt;/div&gt;



&lt;p&gt;At this point we have the ability to create a certificate. It will utilize which ever issuer we want. In the example below, I’m using the letsencrypt-prod which matches the definition above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Certificate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  namespace: default
  name: hello-certs
spec:
  secretName: hello-certs
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - hello.demo.com

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

&lt;/div&gt;



&lt;p&gt;If everything worked correctly you should be able to see the certificate via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get secrets hello-certs -o json

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Update Nginx To Enable SSL
&lt;/h2&gt;

&lt;p&gt;Finally now that we have a certificate, let’s enable nginx to use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-docker
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  tls:
    - hosts:
        - hello.demo.com
      secretName: hello-certs
  rules:
    - host: hello.demo.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hello-docker-svc
                port:
                  number: 80

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

&lt;/div&gt;



&lt;p&gt;At this point you should be able to connect &lt;a href="https://hello.demo.com" rel="noopener noreferrer"&gt;https://hello.demo.com&lt;/a&gt; and get a valid response.&lt;/p&gt;

&lt;p&gt;You can also check the validity of your certificate by testing it via &lt;a href="https://www.ssllabs.com/ssltest/" rel="noopener noreferrer"&gt;SSL Labs&lt;/a&gt; I received in A+ for the certificate I generated&lt;/p&gt;

</description>
      <category>docker</category>
      <category>opensource</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Local Email Development Service</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Sat, 24 Apr 2021 17:30:03 +0000</pubDate>
      <link>https://dev.to/csgeek/local-email-development-service-5ejd</link>
      <guid>https://dev.to/csgeek/local-email-development-service-5ejd</guid>
      <description>&lt;p&gt;Whenever I've worked on any application that sends out emails it's always an issue on how to mimic the behavior on my local laptop.  Usually wherever the app is deployed is configured to be able to just 'work'.  aka you can send email by connecting to localhost with no auth.&lt;/p&gt;

&lt;p&gt;Now, how do I mimic locally? In the past i've tried setting up postfix etc in a docker stack, or more recently doing an smtp relay using google services.&lt;/p&gt;

&lt;p&gt;For reference the previous pattern:&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;#!/usr/bin/env bash &lt;/span&gt;
docker run &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="nt"&gt;--name&lt;/span&gt; mail &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RELAY_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;smtp.gmail.com &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RELAY_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;587 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RELAY_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;user &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RELAY_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 25:25 &lt;span class="se"&gt;\ &lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; bytemark/smtp

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

&lt;/div&gt;



&lt;p&gt;This pattern requires you to enable unsecured application in your google account.&lt;/p&gt;

&lt;p&gt;The new pattern I've started using of late is leveraging the wonderful tool called &lt;a href="https://github.com/mailhog/MailHog" rel="noopener noreferrer"&gt;MailHog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To set it up simply add the following to your docker-compose.yml 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;mail&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;mailhog/mailhog:v1.0.1&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;mail&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;8025:8025&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;1025:1025&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your application is running in docker you don't need to expose 1025, if you're running your app outside of docker, you need to get to 1025 to send mail.&lt;/p&gt;

&lt;p&gt;don't forget to bring it up via &lt;code&gt;docker-compose up -d mail&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At this point you just need to configure your SMTP settings.&lt;/p&gt;

&lt;p&gt;Here's an example snippet that I used for my app.&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;reporting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;csgeek@anyemail-host.com&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;donotreply@foobar.com"&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Report&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;things"&lt;/span&gt;
  &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&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;1025&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Main parts you should note is the hostname is &lt;code&gt;localhost&lt;/code&gt;, and port is &lt;code&gt;1025&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then once everything is done you can connect to port 8025 and retrieve your email.&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%2Fesamir.com%2Fimages%2Fmailhog_dev_pattern%2Fmailhog_screen.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%2Fesamir.com%2Fimages%2Fmailhog_dev_pattern%2Fmailhog_screen.png" alt="Storage Classes"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>LetsEncrypt SSL DNS automation with lego</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Mon, 22 Mar 2021 16:14:51 +0000</pubDate>
      <link>https://dev.to/csgeek/letsencrypt-ssl-dns-automation-with-lego-3bb0</link>
      <guid>https://dev.to/csgeek/letsencrypt-ssl-dns-automation-with-lego-3bb0</guid>
      <description>&lt;p&gt;if you haven't heard about letsencrypt you should.  If you're still serving all your traffic over HTTP you should stop and move over the HTTPS everything.  Honestly at this point there shouldn't be any reason not to use SSL.  The CPU/server cost is minimal and there is tooling that makes this trivial.  I'm going to walk you through the process of setting SSL using nginx and we'll use docker for good measure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies/Tooling
&lt;/h2&gt;

&lt;p&gt;Some familiarity with the following tools would be nice to have but not required.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;nginx&lt;/li&gt;
&lt;li&gt;SSL&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SSL
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://letsencrypt.org/"&gt;LetsEncrypt&lt;/a&gt; provides free SSL/TLS certificate for the world at large as long as you can verify that you are who you are.  They have several tools that validate you, the easiest being running a web server, adding a DNS entry and so on.  &lt;a href="https://certbot.eff.org/"&gt;Certbot&lt;/a&gt; is likely the most famous tool that generates SSL certificate&lt;/p&gt;

&lt;p&gt;Personally I prefer using &lt;a href="https://github.com/go-acme/lego"&gt;Lego&lt;/a&gt; which is a letsencrypt written in Go.  Partly I'm a big fan of Go as a language so I appreciate the developer's choice but also it lets me wildcard certificates and SAN very easily.&lt;/p&gt;

&lt;p&gt;Although you can generate an SSL easily enough in order to get a wildcard DNS verification is much easier and simpler to use.  Though you do need a more legit DNS provider that has an API exposed.  Lego has a long list of supported &lt;a href="https://go-acme.github.io/lego/dns/"&gt;DNS Providers&lt;/a&gt;.  I have used Amazon Route53 and will likely move to a GCP one down the line, but honestly any of them will work fine.&lt;/p&gt;

&lt;p&gt;What is very cool and special about SAN is that it creates a single certificate that supports multiple domains. &lt;/p&gt;

&lt;p&gt;You can run this anywhere but in my case I have it setup in &lt;code&gt;/etc/letsencrypt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first step is obtaining the certificate the first time.&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;cd&lt;/span&gt; /etc/letsencrypt
&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SECRET  &lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"SECRET"&lt;/span&gt;  lego &lt;span class="nt"&gt;--dns&lt;/span&gt; route53 &lt;span class="nt"&gt;--domains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.foobar.org"&lt;/span&gt; &lt;span class="nt"&gt;--domains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"foobar.org"&lt;/span&gt;   &lt;span class="nt"&gt;--email&lt;/span&gt; valid@email run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see I created a wildcard and a &lt;code&gt;foobar.org&lt;/code&gt;.  It's a bit annoying but *.foobar.org does not cover the base domain.  You can enumerate all your domains if you don't want to have a wild card.  Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /etc/letsencrypt
&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SECRET  &lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"SECRET"&lt;/span&gt;  lego &lt;span class="nt"&gt;--dns&lt;/span&gt; route53 &lt;span class="nt"&gt;--domains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"www.foobar.org"&lt;/span&gt; &lt;span class="nt"&gt;--domains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"foobar.org"&lt;/span&gt; &lt;span class="nt"&gt;--domains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mail.foobar.org"&lt;/span&gt; &lt;span class="nt"&gt;--domains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ftp.foobar.org"&lt;/span&gt; &lt;span class="nt"&gt;--email&lt;/span&gt; valid@email run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have your certificate you simply need to ensure to renew it regularly. I created a simple bash scripts to do so.&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;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /etc/letsencrypt/
&lt;span class="nv"&gt;DOMAINS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"domain1 domain2 foobar.org"&lt;/span&gt;
&lt;span class="nv"&gt;AWS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;CHANGEME&amp;gt;
&lt;span class="nv"&gt;AWS_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;CHANGEME&amp;gt;

&lt;span class="k"&gt;for &lt;/span&gt;domain &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$DOMAINS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$AWS_KEY&lt;/span&gt;  &lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AWS_SECRET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  lego &lt;span class="nt"&gt;--dns&lt;/span&gt; route53 &lt;span class="nt"&gt;--domains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--domains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="nt"&gt;--email&lt;/span&gt; user@gmail.com renew
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using AWS has a bit of a cost for using their DNS providers, but with the 3 domains I have I don't think my bill comes to more than maybe $3/month. That's well worth it to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ghost Web Site
&lt;/h2&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.7"&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;www&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;ghost:4.0.1&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;8890:2368"&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;always&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&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;./content:/var/lib/ghost/content/&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;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shared_mysql&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:5.7.29&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;always&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&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:/var/lib/mysql&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we expose a port locally 8890 that is serving HTTP traffic.&lt;/p&gt;

&lt;p&gt;We need a .env file that is referenced in the MySQL and ghost instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;## Database Ghost config
&lt;/span&gt;&lt;span class="py"&gt;database__client&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;mysql&lt;/span&gt;
&lt;span class="py"&gt;database__connection__host&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;shared_mysql&lt;/span&gt;
&lt;span class="py"&gt;database__connection__user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;root&lt;/span&gt;
&lt;span class="py"&gt;database__connection__password&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;testing&lt;/span&gt;
&lt;span class="py"&gt;database__connection__database&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ghost&lt;/span&gt;

&lt;span class="c"&gt;##MySQL config
&lt;/span&gt;&lt;span class="py"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;testing&lt;/span&gt;
&lt;span class="py"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gbfest&lt;/span&gt;


&lt;span class="c"&gt;## Domain config
#url=http://0.0.0.0:8890
&lt;/span&gt;&lt;span class="py"&gt;url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://www.foobar.org&lt;/span&gt;

&lt;span class="c"&gt;## Email settings Production 
## FILL THESE IN as appropriate
#mail__from=donotreply@domain
#mail__transport=SMTP
#mail__options__port=587
#mail__options__host=
#mail_options_service=SMTP
#mail__options__auth__user=
#mail__options__auth__pass=
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we can bring up the website using &lt;code&gt;docker-compose up -d&lt;/code&gt; but to earn brownie points, we'll use a systemd file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Systemd startup script
&lt;/h3&gt;

&lt;p&gt;in &lt;code&gt;/etc/systemd/system&lt;/code&gt; create the following file.  We'll assume you have a local user named docker_user that ideally has a shell of &lt;code&gt;/bin/nologin&lt;/code&gt; that's part of the docker group.&lt;/p&gt;

&lt;p&gt;ghost.service file:&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="o"&gt;[&lt;/span&gt;Unit]
Description &lt;span class="o"&gt;=&lt;/span&gt; Ghost Website
&lt;span class="nv"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker.service
&lt;span class="nv"&gt;Requires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker.service

&lt;span class="o"&gt;[&lt;/span&gt;Service]
&lt;span class="nv"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;simple
&lt;span class="nv"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/docker_user/ghost
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/bin/docker-compose up
&lt;span class="nv"&gt;ExecStop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/bin/docker-compose stop
ExecReload &lt;span class="o"&gt;=&lt;/span&gt;/usr/bin/docker-compose restart
&lt;span class="nv"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker_user
&lt;span class="nv"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker
&lt;span class="nv"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always
&lt;span class="nv"&gt;RestartSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3


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

&lt;/div&gt;



&lt;p&gt;At this point we can start/stop/enable using systemd.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;ghost.service
systemctl status ghost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In otherwords, if you restart your computer your website will still come up.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Final Notes on ghost
&lt;/h3&gt;

&lt;p&gt;At this point we're listening to traffic on &lt;a href="http://localhost:8890"&gt;http://localhost:8890&lt;/a&gt; that we are not exposing to the internet because like good little internet samaritans we have firewall rules that prevents bad actors from getting in.  But we do need to let people wanting to see our website access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nginx SSL Wrapping
&lt;/h2&gt;

&lt;p&gt;If you haven't you should open up port 80 and 443 on your webserver.  We won't be serving traffic on 80 but if someone comes on 80 we should redirect them and in order to do that we need 80 exposed.&lt;/p&gt;

&lt;p&gt;in &lt;code&gt;/etc/nginx/sites-available&lt;/code&gt; we're going to create the following ghost.conf.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# SSL configuration&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="n"&gt;/var/log/nginx/ghost_access.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;error_log&lt;/span&gt; &lt;span class="n"&gt;/var/log/nginx/ghost_error.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/.lego/certificates/_.foobar.org.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/.lego/certificates/_.foobar.org.key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;client_max_body_size&lt;/span&gt; &lt;span class="mi"&gt;50M&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


        &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/html/ghost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt; &lt;span class="s"&gt;index.htm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;www.foobar.org&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;large_client_header_buffers&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="mi"&gt;32k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt;        &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt;        &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt;        &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://0.0.0.0:8890&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;proxy_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;/(data|conf|bin|inc)/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/robots.txt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;allow&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;log_not_found&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;# Block access to "hidden" files and directories whose names begin with a&lt;/span&gt;
        &lt;span class="c1"&gt;# period. This includes directories used by version control systems such&lt;/span&gt;
        &lt;span class="c1"&gt;# as Subversion or Git to store control files.&lt;/span&gt;
        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;(^|/)\.&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;403&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="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;www.foobar.org&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;p&gt;Before this is finalized we need to enable the site.&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;cd&lt;/span&gt; /etc/nginx/sites-enabled
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; ../sites-available/ghost.conf 01_ghost.conf
nginx &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="c"&gt;## Validates config&lt;/span&gt;
systemclt restart nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're essentially redirecting port 80 to 443 and creating a proxy that wraps all traffic around SSL and services traffic for &lt;a href="http://www.foobar.org"&gt;www.foobar.org&lt;/a&gt;.  That also means if we connect to our server by IP we'll get the default page rather then a valid website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backup Strategies
&lt;/h3&gt;

&lt;p&gt;At this point you should be taking a regular SQL dump of your database, and backup the content of your ghost website.  The ghost lab has a export for all your content which you should use regularly before doing any major operations.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Final Notes
&lt;/h2&gt;

&lt;p&gt;I personally like using Google Storage for all my images content.  I'm currently maintaining a copy of the ghost docker image with additional plugins so support GS with plans of adding S3 soon. You can find the source &lt;a href="https://github.com/OSAlt/gb-docker-ghost"&gt;here&lt;/a&gt; and docker images can be found &lt;a href="https://hub.docker.com/r/geekbeacon/gb-ghost"&gt;here&lt;/a&gt;.  The tags are matched to the official ghost docker container.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>ssl</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Awesome Kubernetes Resources</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Mon, 15 Mar 2021 18:41:27 +0000</pubDate>
      <link>https://dev.to/csgeek/awesome-kubernetes-resources-4jmi</link>
      <guid>https://dev.to/csgeek/awesome-kubernetes-resources-4jmi</guid>
      <description>&lt;p&gt;A much more comprehensive list can be found &lt;a href="https://github.com/tomhuang12/awesome-k8s-resources"&gt;here&lt;/a&gt;. This is likely a subset of the items in that repo and a few additions of tools i’ve tried out or want to try out to make my K8s life easier.&lt;/p&gt;

&lt;p&gt;CLI Tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/helm/helm"&gt;Helm&lt;/a&gt; Kubernetes package manager&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/derailed/k9s"&gt;K9s&lt;/a&gt; CLI management tool.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/cloudnativelabs/kube-shell"&gt;kube-shell&lt;/a&gt; integrated shell environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cluster Provisioning&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/kubernetes-sigs/kind"&gt;kind&lt;/a&gt; Docker based kubernetes deployment.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kubernetes/minikube"&gt;minikube&lt;/a&gt; VM based cross-platform kubernetes with addons support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Automation and CI/CD&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/fluxcd/flux2"&gt;flux 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://web.teamhephy.com/"&gt;Hephy&lt;/a&gt; Developer workflow tool&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://skaffold.dev/"&gt;Skaffold&lt;/a&gt; client side deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Testing/Troubleshooting&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/linki/chaoskube"&gt;Chaos Kube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/asobti/kube-monkey"&gt;Kube Monkey&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Backup/Restore&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/vmware-tanzu/velero"&gt;Velero&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Service Mesh:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/istio/istio"&gt;Istio&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Certifications&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://killer.sh/"&gt;killer&lt;/a&gt; Kubernetes Exam Simulator&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>resources</category>
      <category>kubernetes</category>
      <category>cloud</category>
      <category>reference</category>
    </item>
    <item>
      <title>Home Backup Solution</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Thu, 11 Mar 2021 18:04:23 +0000</pubDate>
      <link>https://dev.to/csgeek/home-backup-solution-3c7p</link>
      <guid>https://dev.to/csgeek/home-backup-solution-3c7p</guid>
      <description>&lt;h2&gt;
  
  
  Home Backup Solution
&lt;/h2&gt;

&lt;p&gt;I setup a NAS a while ago using Arch Linux and ZFS migrating away from Synology. It’s really nice to control your data and be able to use more advanced tooling then the limited set of applications that are available from Synology.&lt;/p&gt;

&lt;p&gt;I had S3 backups setup with Synology and wanted to create something similar on the Arch Server.&lt;/p&gt;

&lt;p&gt;I have about 3 TB of data I want to backup and was looking for something that’s secure and is a bit more intelligent than a crontab of &lt;code&gt;aws sync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, on the off chance that my backup takes more than a few days, I wanted it to be smart enough to not execute two jobs at the same time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing Storage Solution
&lt;/h3&gt;

&lt;p&gt;I was initially using S3 but firstly I find the AWS permissioning incredibly convoluted to manage. I started using GCP more so of late and I really like its simplicity. I ended up giving it a go and made sure to use the equivalent of their ‘cold storage’&lt;/p&gt;

&lt;p&gt;From the available list of Storage classes I ended up choosing ‘Archive’&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d1-67ppV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/storage_class.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d1-67ppV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/storage_class.png" alt="Storage Classes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;it Also seems to have a very reasonable pricing for essentially long term storage that does not need to be access very frequently as you can see below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uPyoHPV---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/archive_pricing.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uPyoHPV---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/archive_pricing.png" alt="Storage Pricing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The other reason I chose GS (Google Storage) over S3 is because the &lt;a href="https://www.duplicati.com/"&gt;tool&lt;/a&gt; I ended up choosing didn’t support S3 coldstorage backup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backup Service
&lt;/h3&gt;

&lt;p&gt;Duplicati is a really cool project though it can be a bit tricky to setup. I ended up utilizing docker-compose and docker to get it up and running.&lt;/p&gt;

&lt;p&gt;Here’s my simple little setup.&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.7"
services:
  duplicati:
    image: ghcr.io/linuxserver/duplicati
    container_name: duplicati
    hostname: duplicati
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Los_Angeles
# - CLI_ARGS= #optional
    volumes:
      - ./config:/config
      - /backups:/backups
      - /tank:/source
    ports:
      - 8200:8200
    restart: unless-stopped

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

&lt;/div&gt;



&lt;p&gt;Please note the mounts. /backups is the destination where you want to store data. I’m mainly using it for the cloud storage so that is mainly pointless. /tank is my ZFS mount point which is mapped to /source and of course the configuration is exposed as a local volume.&lt;/p&gt;

&lt;h4&gt;
  
  
  Systemd Service
&lt;/h4&gt;

&lt;p&gt;Place the following file under /etc/systemd/system/duplicati.service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description = Duplicati
After=docker.service
Requires=docker.service

[Service]
Type=idle
WorkingDirectory=/home/docker_user/backup
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose stop
ExecReload =/usr/bin/docker-compose restart
User=docker_user
Restart=always
RestartSec=3
RestartPreventExitStatus=0
TimeoutStopSec=10

[Install]
WantedBy=multi-user.target

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

&lt;/div&gt;



&lt;p&gt;As you can see I’m running this as a special user I created with limited permissions only being used for running docker applications.&lt;/p&gt;

&lt;p&gt;Let’s enable the service using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemclt enable duplicati

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

&lt;/div&gt;



&lt;p&gt;and of course if you haven’t done so already you should start 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;systemctl start duplicati

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  BackUp Data
&lt;/h2&gt;

&lt;p&gt;Once duplicati is running it’ll be accessible via at port 8200. My NAS’s local ip, is 192.168.1.200, so I can access it via &lt;a href="http://192.168.1.200:8200"&gt;http://192.168.1.200:8200&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see something that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T4ez1lRO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T4ez1lRO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati.png" alt="Duplicati"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Encryption
&lt;/h3&gt;

&lt;p&gt;When you create a backup it’ll ask you to enter an Encryption key if you would like. GS is already private but the added level of encryption doesn’t hurt. Like all secrets make sure you remember the key in case you need it for disaster recovery.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lQWA3lml--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati_cipher.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lQWA3lml--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati_cipher.png" alt="Duplicati_cipher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Destination
&lt;/h3&gt;

&lt;p&gt;The next screen lets you setup the GS bucket. You can use any number of alternative offsite solution ranging from a local backup, (s)ftp, S3, Azure, webdav and many more. You can find the full list &lt;a href="https://duplicati.readthedocs.io/en/latest/01-introduction/#supported-backends"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you use GS, then authorization is made very simply by simply clicking on the Auth key will generate a token for duplicati to use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Nb67Ep_4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati_google_storage.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Nb67Ep_4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati_google_storage.png" alt="Duplicati_storage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Source
&lt;/h3&gt;

&lt;p&gt;The next two steps i’ll skip over as they’re fairly straight forward. Just choose the Source data. If you remember you mounted your Raid/ZFS as /source and the destination if you did mount it should be /backup. Navigation has a mild &lt;code&gt;windows&lt;/code&gt; feel with &lt;code&gt;My Computer&lt;/code&gt; replacing the typical root but otherwise this should feel familiar. You can add a single or multiple paths.&lt;/p&gt;

&lt;p&gt;Please note, that the output will be stored in a duplicati special format. You won’t be able to go to the backup location and simply look at the files without Duplicati restoring the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Schedule
&lt;/h3&gt;

&lt;p&gt;The schedule is pretty easy to setup. Though it would be nice if they supported cron expression, I found their UI pretty flexible and configurable for every pattern that I was interested in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o6z2WP27--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati_schedule.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o6z2WP27--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati_schedule.png" alt="Duplicati_Schedule"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Backup Retention
&lt;/h3&gt;

&lt;p&gt;Finally the backup retention. You can set it up so backups older than so many days are deleted, or ensure that you have no more than N number of backups etc. &lt;code&gt;Smart backup retention&lt;/code&gt; is pretty cool as well keeping one copy of the last 7 days, one copy of the last 4 weeks and one copy of the last 12 months. Personally since i’m storing about 3 TB of data as it is, I’m only keeping one copy remotely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WUrahPc4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati_retention.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WUrahPc4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.esamir.com/images/home_backup_solution/duplicati_retention.png" alt="Duplicati_Retention"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Step, Backup Duplicati
&lt;/h2&gt;

&lt;p&gt;I also wanted to backup my Duplicati installation. Initially I had setup duplicati to save to my NAS as a local backup but since I need duplicati to restore it I ended up just adding a little cron + rsync to ensure that I have a copy of the settings in case I do something extra dumb and destroy my docker installation of duplcati.&lt;/p&gt;

&lt;h2&gt;
  
  
  References/ Requirements
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.duplicati.com/"&gt;Duplicati&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/"&gt;Google Cloud&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>linux</category>
      <category>tutorial</category>
      <category>opensource</category>
      <category>cloudskills</category>
    </item>
    <item>
      <title>Project Management Tools</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Tue, 19 Jan 2021 18:29:30 +0000</pubDate>
      <link>https://dev.to/csgeek/project-management-tools-1g1k</link>
      <guid>https://dev.to/csgeek/project-management-tools-1g1k</guid>
      <description>&lt;p&gt;As a software developer I've been using Jira for a very long time and I do like it partly because of its features and partially due to familiarity.  I do find the basic concepts from Jira missing in far too many project management tools.  &lt;/p&gt;

&lt;p&gt;I have gotten into conversations with other volunteers and developers about what to use and it seems everyone has a strong dislike for Jira/Atlassian but I have yet to find any tool that does just the basic things I need.&lt;/p&gt;

&lt;p&gt;Requirement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sprint Support&lt;/li&gt;
&lt;li&gt;A backlog (this seems to be very rare).  Just to be clear, a column on your board named backlog isn't a backlog.  I want a backlog that is out of view until we talk about the tickets then the 'board' is only items you need to look at. &lt;/li&gt;
&lt;li&gt;A board with states / transitions.  ie: ToDo, InProgress, QA, Review, Done etc...&lt;/li&gt;
&lt;li&gt;Nice to have: Timeline / Gant chart to track dependencies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point I've tried using:  Asana, Trello, Wrike, Taiga and Jira.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breakdown
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Trello: a cute toy to get organized but becomes absolute nightmare to manager after a few months.  Sprints and other advanced features are available via addons but they're basically hacks / after thoughts added in later.&lt;/li&gt;
&lt;li&gt;Asana: nice idea but it seems to want to want to be too many things.  Calendar, Forms, Inbox, Board etc.  It does have some nice features. &lt;/li&gt;
&lt;li&gt;Wrike: Same overly complicated and missing key features unless you pay.  Various views to see the same tickets but not really a way to do sprints.&lt;/li&gt;
&lt;li&gt;Taiga: The ONLY one so far that does a backlog.  It fails in that it has too many issue types and the grouping is weird.  Certain types of tickets/task/issues/stories are only visible in certain areas and you need to convert the ticket from type A to type B to pull it into the sprint and such.  It is able to pull from github and does a 2 way sync which is very cool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So at this point the only reasonable tool i've ended up with is Jira that it seems everyone hates but honestly I haven't found the features i need anywhere else.  What do people use for real project management that isn't a toy short lived project?&lt;/p&gt;

</description>
      <category>management</category>
      <category>project</category>
      <category>software</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Toxicity Of Open Source</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Mon, 28 Dec 2020 19:37:22 +0000</pubDate>
      <link>https://dev.to/csgeek/the-toxicity-of-open-source-2ae</link>
      <guid>https://dev.to/csgeek/the-toxicity-of-open-source-2ae</guid>
      <description>&lt;p&gt;My most recent experiences though really saddened me and exposed a certain toxicity in the OSS community that I really wish wasn't so prevalent. &lt;/p&gt;

&lt;p&gt;I've been posting on and off on dev.to but I never introduced myself so let me preface this post with a bit about me to give some context. &lt;/p&gt;

&lt;p&gt;I've been using open source and Linux on and off since the early 2000s.  My first linux distro was Caldera 2.4 an old RPM based that was cool in its hayday and then turned towards the dark side with various different lawsuits against Linux that never went anywhere.&lt;/p&gt;

&lt;p&gt;I've been involved in more Linux Users Groups, mailing lists and conferences then I can count.  I helped start a Linux conference that's been running for a good while after I graduated educating and teaching people about the benefits of Linux and Open Source.&lt;/p&gt;

&lt;p&gt;Though the pandemic climate makes it difficult to have the same experience.  Planning a conference exposed me to open source and open culture in a way that going to LUGs never had.  I had the opportunity to get a true grasp of what the Free Software Foundation (FSF) truly stands for and talk and meet with some of the key figures of Linux/OSS movement.&lt;/p&gt;

&lt;p&gt;I've always appreciated Open Source because it provides me as a user with (first off an alternative to commercial programs) and the freedom to run, edit, contribute and share (the 4 FSF freedoms).  I've always appreciated the freedom of choice that Linux has.  Nobody needs 15 editors, but I really do love that there are 15 different ways to add 'hello world' to a text document.&lt;/p&gt;

&lt;p&gt;We've built up a community that is all about the freedom of expression, freedom of choice, freedom to use code and software in anyway you desire with the appropriate permissive license.  So it's always sad when you hear and see people bashing other users because they don't agree with their choices.  Yes we have our mini flame wars of vim vs emacs vs nano.  Some people take it a bit too seriously but at the end of the day most respect other's people choice to use whatever they like and move on. (I would like to think at least)&lt;/p&gt;

&lt;p&gt;I volunteered to help with an Open Source conference and they rallied last minute to try and figure out what they could do this year since everyone is quarantined still.  They spent their time and researched the best tools they could find given the time, resources, and skillset available to them and ended up choosing Zoom.  I know, not my favorite tool either but at the end of the day, it's a well tested tool that works. They ran an entire 3 day event on zoom that had the founder of redhat, the fsf and so many more incredible speakers sharing their knowledge and expertise.  Yet, somehow all of that was eclipsed by the rants about the platform they chose.  &lt;/p&gt;

&lt;p&gt;There were so many tweets and messages I ran across of that were complaining about the tooling.  Yes, we know that Zoom isn't open source, but if we're using zoom to show a video from key figures in OSS speaking about relevant topics that is important to them and zoom is allowing us to share that content with a wider audience.  Does it matter that much that it's closed source? If we value a user's ability to choose, shouldn't we actually let the people choose.  &lt;/p&gt;

&lt;p&gt;You can't scream from the mountain tops about how amazing Linux is, and how you appreciate the freedom of choice, then look condescendingly at the poor plebeian who chose to use Windows.  &lt;/p&gt;

&lt;p&gt;Sure, not my favorite operating system.  I do find it limiting to my day to day tasks, but that's my experience.  There might be a legitimate reason that an individual may NEED to use windows, or hell maybe they just like it.  They honestly don't even need to justify their choice.  They DO deserve to be able to say they are using windows, mac, photoshop, office or honestly any piece of software and any technology without being put on the defensive about it.  There is nothing I can imagine that would hurt the Linux community more than this false sense to elitism that makes people dismiss others because their values or choices don't align with theirs.&lt;/p&gt;

&lt;p&gt;I've gotten into so many conversation with people in the community about this.  If you value the freedom of choice, then you can't be upset that the person's choice doesn't line with your views.&lt;/p&gt;

&lt;p&gt;Am I completely off base here or am I missing something in the OSS community? &lt;/p&gt;

&lt;p&gt;The year of the "Linux Desktop" will never come if every time someone is curious about our community we respond with antagonism, judgement, and all around toxicity.  I'm naturally speaking about a vocal minority of the community but I do want to bring it to attention.  Those few individuals no matter how brilliant and capable they might be are doing, in my opinion, more harm than good.  &lt;/p&gt;

</description>
      <category>linux</category>
      <category>opensource</category>
      <category>communities</category>
    </item>
    <item>
      <title>Newbie Question</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Mon, 05 Oct 2020 16:42:10 +0000</pubDate>
      <link>https://dev.to/csgeek/newbie-question-2jg5</link>
      <guid>https://dev.to/csgeek/newbie-question-2jg5</guid>
      <description>&lt;p&gt;Maybe someone can enlighten me on this, I've seen multiple people create linked articles where it's a 5 part tutorial and such.&lt;/p&gt;

&lt;p&gt;How does one associate two articles together?  I actually just wanted to add an addendum but knowing how to create a multi-part series would be very helpful.&lt;/p&gt;

</description>
      <category>help</category>
      <category>writing</category>
    </item>
    <item>
      <title>Github Actions vs Travis CI</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Tue, 22 Sep 2020 02:09:09 +0000</pubDate>
      <link>https://dev.to/csgeek/github-actions-vs-travis-ci-epd</link>
      <guid>https://dev.to/csgeek/github-actions-vs-travis-ci-epd</guid>
      <description>&lt;p&gt;I was wondering if anyone would mind sharing their thoughts on Github Actions.&lt;/p&gt;

&lt;p&gt;I've been using Travis CI for a while and it seems to work fairly well but every once in a while I really would love to use something that's more docker centric which I think Github actions is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Travis CI
&lt;/h2&gt;

&lt;p&gt;free for open source projects, you can easily set secrets to allow for a variety of different patterns.  Downside is that it seems like what a bit of a blackbox on what version of X come with their images.  Especially once you start doing Matrix configuration (ie. I need to run a python test, golang, and ruby test for my code base)&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Action
&lt;/h2&gt;

&lt;p&gt;Much younger than Travis CI.  It seems pretty green when I tried it out initially but I am curious of people's thoughts on it.  &lt;/p&gt;

&lt;p&gt;I did notice that it was using a more docker based approach.  So maybe it's worth giving it another go.  &lt;/p&gt;

&lt;p&gt;Thoughts? Comments? Any love or hate ? Open to other solutions, ideally free/OSS.  &lt;/p&gt;

</description>
      <category>discuss</category>
      <category>help</category>
    </item>
    <item>
      <title>Blog Posting Platform</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Sat, 19 Sep 2020 19:24:43 +0000</pubDate>
      <link>https://dev.to/csgeek/blog-posting-platform-5f8k</link>
      <guid>https://dev.to/csgeek/blog-posting-platform-5f8k</guid>
      <description>&lt;p&gt;I was debating between two platforms that essentially do the same thing which is take Markdown -&amp;gt; shiny.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docusaurus
&lt;/h2&gt;

&lt;p&gt;The first one I've used heavily for documentation purposes but rarely for blogging. It's a project from facebook called &lt;a href="https://v2.docusaurus.io/"&gt;Docusaurus&lt;/a&gt; (Linking their beta version because its just so much prettier ).&lt;/p&gt;

&lt;p&gt;I feel like it's more geared at writing docs than blogging, but here's the pluses.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write once in MD and get pretty output.&lt;/li&gt;
&lt;li&gt;Easy to deploy to github.io and other hosting sites.&lt;/li&gt;
&lt;li&gt;I supports jsx so you can embed quasi html code inside your markdown.
&lt;/li&gt;
&lt;li&gt;Since it's a react app you utilize react component to enhance your blog.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Although it has advanced features like jsx and react components, the source .md will not really look right on github for example.  You'll need to generate it to see the whole picture. (Though it's the price you pay for it)&lt;/li&gt;
&lt;li&gt;I can't seem to manage to every spell docusaurus correctly no matter how many times I type it out. :\ personal issue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hugo
&lt;/h2&gt;

&lt;p&gt;I haven't explored this fully but it's a markdown generator written in Go (big win for me).  It's super fast though I doubt I'll need to worry about compiling 20,000 md files anytime soon. &lt;/p&gt;

&lt;p&gt;There are also interesting tooling like &lt;a href="https://github.com/wowchemy/wowchemy-hugo-modules"&gt;Hugo Academic&lt;/a&gt; which provides an easy way of creating content.  (Then again, VSCode works just as well for starting out )&lt;/p&gt;

&lt;p&gt;It also seems to have a much wider array of themes to choose from, though most of them are giving me issues, likely a noob problem.&lt;/p&gt;

&lt;p&gt;Any thoughts on these two or any other ones you'd like to toss in the mix?&lt;/p&gt;

&lt;p&gt;Honestly the main goal is to write in .md push to some 'origin' self hosted version and able to also publish to say dev.to, medium, and other fun platforms i post on.&lt;/p&gt;

</description>
      <category>writing</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Rapid Development + Scaling Web</title>
      <dc:creator>csgeek</dc:creator>
      <pubDate>Mon, 31 Aug 2020 21:19:56 +0000</pubDate>
      <link>https://dev.to/csgeek/rapid-development-scaling-web-32g1</link>
      <guid>https://dev.to/csgeek/rapid-development-scaling-web-32g1</guid>
      <description>&lt;p&gt;What does everyone use these days for rapid development for the web?&lt;/p&gt;

&lt;p&gt;I tend to have strong feelings against wordpress and similar CMS, but end up needing a CMS like features to allow people to post, login, moderate etc.&lt;/p&gt;

&lt;p&gt;I'm not stuck with any technology but I know i'm not great on the frontend side, so I'd love to find something that's fast to get something out while not being a nightmare to scale up if we need to down the line (Like Wordpress for example )&lt;/p&gt;

&lt;p&gt;--&lt;br&gt;
csgeek&lt;/p&gt;

</description>
      <category>help</category>
      <category>discuss</category>
      <category>linux</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
