<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sam Texas</title>
    <description>The latest articles on Forem by Sam Texas (@heysamtexas).</description>
    <link>https://forem.com/heysamtexas</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%2F203915%2F648b8ba2-9f1e-4273-8575-f98964ab30ce.png</url>
      <title>Forem: Sam Texas</title>
      <link>https://forem.com/heysamtexas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/heysamtexas"/>
    <language>en</language>
    <item>
      <title>Data is like deep fryer refuse, not oil. People closest to the data can refine it.</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Wed, 07 Aug 2024 09:40:28 +0000</pubDate>
      <link>https://forem.com/heysamtexas/data-is-like-deep-fryer-refuse-not-oil-people-closest-to-the-data-can-refine-it-lo5</link>
      <guid>https://forem.com/heysamtexas/data-is-like-deep-fryer-refuse-not-oil-people-closest-to-the-data-can-refine-it-lo5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjq4s3z669dzb7n4cngy.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjq4s3z669dzb7n4cngy.webp" alt="Data is like deep fryer refuse, not oil. People closest to the data can refine it." width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Data is Not the New Oil - More Like Deep Fryer Refuse&lt;/p&gt;

&lt;h3&gt;
  
  
  The Grim Reality
&lt;/h3&gt;

&lt;p&gt;Let's burst that bubble: Your data is far from being the light, sweet crude you're dreaming of. Think less "Spindletop gushes riches" and more "last night's deep fryer leftovers." That goopy, lumpy mess of data you've got? It's filled with impurities and inconsistencies. Seriously, if data were oil, yours would make Exxon weep.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Sober Reckoning
&lt;/h3&gt;

&lt;p&gt;Now, let's take a hard look at where you stand. I’m betting only a handful in your organization, probably the tech-savvy folks buried in the trenches, can distinguish the gold from the garbage. Clarity often begins at the bottom of the org chart. Yet, leaders frequently miss the forest for the trees—they don’t know where to dig, how to engage, or even what questions to ask.  &lt;/p&gt;

&lt;p&gt;Our increasing awareness of our business mortality hits home. That tranquil period of Zero Interest Rate Policy (ZIRP) was a lullaby we now must wake from. It’s time to face reality.&lt;/p&gt;

&lt;h3&gt;
  
  
  No Mary Poppins for Data Cleanup
&lt;/h3&gt;

&lt;p&gt;Tossing your data into the AI pot won't magically transform it into a treasure trove of insights. Best case, you'll get a machine that's slightly better than your average high school senior at writing grammatically correct sentences. Worst case, it's all smoke and mirrors with no lasting value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rolling Up Your Sleeves
&lt;/h3&gt;

&lt;p&gt;Here’s the deal: the transformation begins with your own people. Engage with those individual contributors at the bottom of the org chart—the ones who are knee-deep in where the data is born. They'll help structure, clean, and contextualize your data, turning that lumpy fryer oil into high-grade crude.&lt;/p&gt;

&lt;p&gt;And let's not sugarcoat it: it’s a refining process. Skipping this part is like trying to ace a college course without ever cracking open a book. The students who walk into the lecture prepared are the ones who truly grasp the material.&lt;/p&gt;

&lt;p&gt;In short, there’s no snapping of fingers for pristine data sets. It’s a grind, a journey that starts from the ground up. But trust me, this groundwork sets the stage for any AI wizardry you may want to pull off down the line.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Startups and scale-ups are not supposed to fight fair</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Mon, 15 Jul 2024 14:13:33 +0000</pubDate>
      <link>https://forem.com/heysamtexas/startups-and-scale-ups-are-not-supposed-to-fight-fair-nah</link>
      <guid>https://forem.com/heysamtexas/startups-and-scale-ups-are-not-supposed-to-fight-fair-nah</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8rhqtpzfday0a31srqz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8rhqtpzfday0a31srqz.webp" alt="Startups and scale-ups are not supposed to fight fair" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  We have playbooks and templates purpose built for situations and seasons you have not yet seen, but you will
&lt;/h3&gt;

&lt;p&gt;The road from scrappy startup to enterprise player isn’t paved but filled with hurdles and unexpected surprises. A seasoned CTO comes armed with playbooks, templates, and built-in Spidey-sense that is tailor-made for situations you've never seen but inevitably will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Testing hypotheses&lt;/strong&gt; : Patterns and tactics for rapid prototyping isn’t just a buzzword; it’s survival.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rapid iteration&lt;/strong&gt; : Iterate until you can tattoo &lt;em&gt;"Perfect is the enemy of good"&lt;/em&gt; on your forearm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maturing from proof-of-concept to production readiness&lt;/strong&gt; : It's like graduating from a paper plane to a spacecraft.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These templates help you see around corners and over the horizon, covering scaling, security, and compliance—the "unknown unknowns" you’d rather not think about.&lt;/p&gt;

&lt;h3&gt;
  
  
  We have networks built up over years in multiple industries and across multiple disciplines
&lt;/h3&gt;

&lt;p&gt;It’s not just financial capital that keeps your startup afloat; there’s also human and social capital. A Fractional CTOs network is stretches wide and deep across industry, government, regulatory, and even those murky in-between places.&lt;/p&gt;

&lt;p&gt;A Fractional CTO's network is stretches wide and deep across industry, government, regulatory, and even those murky in-between places.&lt;/p&gt;

&lt;p&gt;That social equity and trust is earned in drops and spent (or lost) in buckets. A seasoned operator knows how to allocate and deploy it wisely and effectively – to the benefit of the business and the team.&lt;/p&gt;

&lt;p&gt;Don't sleep on this. You don't get far by money alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  🐈 🐈 🐈 We have lived multiple lives and made pivots before landing in your Slack channel
&lt;/h3&gt;

&lt;p&gt;You’re not just getting a tech leader; you might be getting a professional copywriter, marketer, behavioral psychologist, and even a part-time therapist. Seriously.&lt;/p&gt;

&lt;p&gt;✍ Copywriting: Stuck on that landing page copy? Your Fractional CTO might just moonlight as a Mad Men alum.&lt;/p&gt;

&lt;p&gt;🧠 Behavioral insights: Gleaning user behavior to refine product-market fit.&lt;/p&gt;

&lt;p&gt;🚨 Crisis management: Parenting-level de-escalation skills—trust me, they come in handy.&lt;/p&gt;

&lt;p&gt;It make sense when you think about it – there is a lot in that Mary Popping bag than just the one thing. Make sure you ask about it, there could be a lot more that is not mentioned on that CV/resume.&lt;/p&gt;

&lt;h3&gt;
  
  
  We Leading Like Missionaries, Executing Like Mercenaries
&lt;/h3&gt;

&lt;p&gt;Hourly consultants come in all tactical and no soul, like Seal Team Six. But a Fractional CTO brings that “Moses with tactical gear” swagger. They’re in for the mission, not just the hourly rate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seal Team Six vibes&lt;/strong&gt; : Task assassins who move the needle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Moses archetype:&lt;/strong&gt; Guiding your team through the wastelands of market uncertainty to the promised land of product-market fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broader vision:&lt;/strong&gt; Catching weekend calls to soothe your 2 AM product existential crises.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bang for Your Buck
&lt;/h3&gt;

&lt;p&gt;We’re no longer in a ZIRP market where every pitch deck filled with Blockchain buzzwords gets funded. A Fractional CTO provides value that far outstrips the cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Affordable expertise&lt;/strong&gt; : Tap into a seasoned leader's wisdom for a fraction (pun intended) of the full-time cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Life-stage advantages&lt;/strong&gt; : Many Fractional CTOs opt for this setup due to other life obligations—kids, aging parents, you name it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Win-win&lt;/strong&gt; : It’s a mutually beneficial deal, allowing startups to leverage high-caliber talent without breaking the bank.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>5 Things I Will Never Do Again as a CTO or Senior manager</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Tue, 02 Jul 2024 14:16:31 +0000</pubDate>
      <link>https://forem.com/heysamtexas/5-things-i-will-never-do-again-as-a-cto-or-senior-manager-26n9</link>
      <guid>https://forem.com/heysamtexas/5-things-i-will-never-do-again-as-a-cto-or-senior-manager-26n9</guid>
      <description>&lt;p&gt;Becoming a good manager is not only about learning what to do. It is also what not to do. These are some of my hardest-won lessons as I write this as a diary to myself.&lt;/p&gt;

&lt;h3&gt;
  
  
  I will never again Be the Sole Agent of Culture Creation or Change
&lt;/h3&gt;

&lt;p&gt;As a senior manager or CTO, I've learned that driving company culture isn't my primary responsibility. While I can and should influence the engineering culture within my team, overall company culture should be shaped and driven by the CEO. In a previous role as a non-co-founding CTO, I made the mistake of trying to take on the mantle of culture agent for the entire company. This role belongs to the CEO, and if they don't take it up, it's not my responsibility to fill that gap. My focus should remain on fostering a positive and productive culture within the engineering department, aligning with the broader vision set by the leadership.&lt;/p&gt;

&lt;h3&gt;
  
  
  I will never again Start Greenfield Projects with Microservices
&lt;/h3&gt;

&lt;p&gt;For greenfield projects, I’ve learned the hard way that starting with microservices can lead to unnecessary complexity and overhead. Instead, I will always begin with a templated monolith and iterate from there. This approach allows for faster development and easier debugging in the early stages. As the project grows and the need for scalability becomes more apparent, we can then consider breaking the monolith into microservices. This strategy ensures we don't prematurely optimize and can adapt more flexibly to the project’s evolving requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  I will never again seek out 10x engineers
&lt;/h3&gt;

&lt;p&gt;I’ve come to believe that the concept of a "10x engineer" is more myth than reality—hallucinations of Silicon Valley fever dreams and Substack newsletters. Instead of chasing these unicorns, I prefer to take a "Moneyball" approach, akin to Brad Pitt's character in the movie, and focus on building a team of solid performers who can execute consistently and predictably. This approach ensures that the organization can effectively absorb their output, and customers can handle the changes and deliveries we make at a steady, reliable pace. It’s about building a balanced team where each member contributes to a cohesive, well-functioning unit rather than relying on the perceived exceptional output of a few individuals.&lt;/p&gt;

&lt;h3&gt;
  
  
  I will never again Dictate How Things Should Be Done
&lt;/h3&gt;

&lt;p&gt;I’ve learned that as a CTO, my role is not to dictate how my team should accomplish tasks, but to clearly define and enforce the desired outcomes. My focus should be on setting clear goals and objectives, ensuring everyone understands what success looks like. This empowers the team to leverage their expertise and creativity to find the best path to achieve those outcomes. By emphasizing results over methods, I foster a more innovative and motivated team that is aligned with our overall vision and goals.&lt;/p&gt;

&lt;p&gt;Additionally, I understand that my team might not always know how to achieve these outcomes. Therefore, I support them by providing learning and development opportunities. This could involve buying them books, enrolling them in courses, or offering other resources that help them build the skills needed to reach our goals. Supporting my team in this way ensures they have the knowledge and tools necessary to succeed, and it demonstrates my commitment to their professional growth.&lt;/p&gt;

&lt;h3&gt;
  
  
  I will never again Blur the Lines Between Professional and Personal Relationships
&lt;/h3&gt;

&lt;p&gt;I’ve learned the importance of maintaining professional distance with my team members. I will never again become close friends or deeply engage with them on a personal level. While building rapport and understanding is crucial, it is equally important to preserve professional boundaries.&lt;/p&gt;

&lt;p&gt;From a psychological perspective, blurring these lines can lead to various issues. Personal friendships within a professional context can create biases, favoritism, and conflict of interest, which can undermine team dynamics and decision-making. It can also make it challenging to provide objective feedback or make tough decisions that may impact those personal relationships.&lt;/p&gt;

&lt;p&gt;Moreover, maintaining professional distance helps in preserving authority and respect. When the boundaries are clear, it becomes easier to lead effectively, as team members are less likely to question decisions based on personal feelings.&lt;/p&gt;

&lt;p&gt;In essence, while it’s important to be empathetic and supportive as a leader, it’s equally crucial to keep the relationship professional to ensure fairness, objectivity, and the ability to lead without personal entanglements clouding judgment.&lt;/p&gt;

</description>
      <category>leadership</category>
    </item>
    <item>
      <title>Docker, Django, Traefik, and IntercoolerJS is My Go-To Stack for Building a SaaS in 2021</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Sat, 30 Jan 2021 21:25:00 +0000</pubDate>
      <link>https://forem.com/heysamtexas/docker-django-traefik-and-intercoolerjs-is-my-go-to-stack-for-building-a-saas-in-2021-52aj</link>
      <guid>https://forem.com/heysamtexas/docker-django-traefik-and-intercoolerjs-is-my-go-to-stack-for-building-a-saas-in-2021-52aj</guid>
      <description>&lt;p&gt;This article has been updated. It was previously dated for 2020. Very little has changed, but I call it out where needed.&lt;/p&gt;

&lt;p&gt;I recently published some thoughts on &lt;a href="https://dev.to/simplecto/docker-django-traefik-and-intercoolerjs-is-my-go-to-stack-for-building-a-saas-in-2020-pdf"&gt;Django being a great framework for applications&lt;/a&gt;. This post expands on that to include the other pieces of infrastructure from development to production environments.&lt;/p&gt;

&lt;p&gt;I have used this stack (or ones that look a lot like it) to build small SaaS apps in 2018, 2019, 2020, and now in 2021.&lt;/p&gt;

&lt;h3&gt;
  
  
  My entire stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Linux Server/VM Hosted anywhere (I like Azure, Digital Ocean, or Scaleway) &lt;em&gt;(edit: 2021 – I'm in the process of moving everything over to a dedicated server at &lt;a href="https://www.hetzner.com" rel="noopener noreferrer"&gt;Hetzner&lt;/a&gt;.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Docker. Just plain docker&lt;/li&gt;
&lt;li&gt;Traefik for reverse proxy and TLS with LetsEncrypt&lt;/li&gt;
&lt;li&gt;Postgresql running in docker&lt;/li&gt;
&lt;li&gt;Django in a container&lt;/li&gt;
&lt;li&gt;Intercoolerjs for easy and slick Ajax-like front-end work &lt;em&gt;(Edit: 2021 - the creator of intercooler has released &lt;a href="https://htmx.org" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt;, the successor to IntercoolerJS.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Sentry for catching production bugs (easy three-lines added to your config)&lt;/li&gt;
&lt;li&gt;Bitbucket pipelines for CI/CD &lt;em&gt;(Edit: 2021 - I no longer bother with CI / CD for personal projects. It it too much tooling for no benefit)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;ZeroTier for VPN/ControlPlane&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For smaller projects I run tests locally in docker containers and then push directly into production. I  don't bother with full CI/CD because I don't need the complexity of it all. That said, I do like Bitbucket Pipelines.&lt;/p&gt;

&lt;p&gt;Woah! that is a lot to unpack here. Let's visualize this another way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual Machine

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Django

&lt;ul&gt;
&lt;li&gt;Volume mounted data disks&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Workers (Long-running django commands)

&lt;ul&gt;
&lt;li&gt;Volume mounted data disks&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Postgres

&lt;ul&gt;
&lt;li&gt;Volume-mounted data disks&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Traefik&lt;/li&gt;

&lt;li&gt;Zero-Tier&lt;/li&gt;

&lt;li&gt;SSH&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Was this helpful? Let me know...&lt;/p&gt;

&lt;h3&gt;
  
  
  Hosting
&lt;/h3&gt;

&lt;p&gt;Your stuff needs a home (yes, even in the "serverless" world. LOL). My personal preferences are &lt;a href="https://azure.com" rel="noopener noreferrer"&gt;Azure&lt;/a&gt;, &lt;a href="https://m.do.co/c/c844c6492d23" rel="noopener noreferrer"&gt;Digital Ocean&lt;/a&gt; &lt;em&gt;(affiliate link)&lt;/em&gt;, or &lt;a href="https://scaleway.com" rel="noopener noreferrer"&gt;Scaleway&lt;/a&gt;. They each offer enough compute, networking options, storage, and basic services to build out proofs-of-concept or whatever you might need.&lt;/p&gt;

&lt;p&gt;Another honorable mention here is &lt;a href="https://hetzner.com" rel="noopener noreferrer"&gt;Hetzner&lt;/a&gt;. They offer a good level of hardware, service and price.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Virtual Machines
&lt;/h3&gt;

&lt;p&gt;For those side projects and many enterprise applications, &lt;strong&gt;scale is not an issue&lt;/strong&gt;. That means that I will not serve thousands of simultaneous users or handling terabytes of data. Therefore I can get by on the smaller offerings – usually under $20/month. Even Azure (the most expensive of the three) offer their burstable VMs. Generally I like to go with &lt;a href="https://www.scaleway.com/en/virtual-instances/development/" rel="noopener noreferrer"&gt;Scaleway's Developer line of servers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;_ &lt;strong&gt;Notice that Kubernetes is missing from my stack? When scale is not an issue then you don't need Kubernetes.&lt;/strong&gt; _&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker. Just plain Docker.
&lt;/h3&gt;

&lt;p&gt;I do not rely on the OS vendor (Ubuntu) to make sure that I run the latest Docker on new VMs. Therefore I use the nice little &lt;code&gt;curl|bash&lt;/code&gt; technique.&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 https://get.docker.com | sudo bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This cure one-liner will get the best and most recent version for your machine running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traefik for Reverse Proxy
&lt;/h3&gt;

&lt;p&gt;Traefik has been a God-send since I found it. Nginx is great, but it was not built for the Docker universe. Traefik has two killer features that have saved me hours upon hours:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Automatic TLS with LetsEncrypt. Literally set-it-and-forget-it. With the right API keys and DNS provider you can also do verification with DNS.&lt;/li&gt;
&lt;li&gt;Automatic no-reload configuration using docker labels. When you spin up new services Traefik will pickup the changes automatically because it listens to all Docker-related events. This makes it incredibly convenient to add, remove, or merge services as needed without any hassle.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My only comment on Traefik is that there is a bit of a learning curve. You have to decide how you want to configure it (config file, command line options, yaml, or docker labels, or use a combination!)&lt;/p&gt;

&lt;p&gt;Another note here: I have already &lt;a href="https://dev.to/simplecto/my-configuration-for-traefik-2-0-docker-and-letsencrypt-285d"&gt;published my production configuations for Traefik&lt;/a&gt; here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Postgres for Database
&lt;/h3&gt;

&lt;p&gt;Tried and True, PostgreSQL has never let me down. I usually attach one of these containers to a project that needs it without any difficulty. I simply spin up the container, bind the ports, and then bind the data volume to my host disk. Done and Done.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker-compose.yml
&lt;/h3&gt;



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

services:

  db:
    container_name: postgres
    hostname: postgres
    image: postgres:11
    restart: always
    environment:
      POSTGRES_PASSWORD: secretsonly
    volumes:
      - ./data:/var/lib/postgresql/data
    ports:
      - 5432:5432
    networks:
      - web

networks:
    web:
        external: true

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dockerized Django for Web
&lt;/h3&gt;

&lt;p&gt;Docker deploys nicely in a container, and I have been doing it for a few years now. The benefits of matching your development environment to your production one cannot be overstated, and I have Docker to thank for that.&lt;/p&gt;

&lt;p&gt;Edit: 2021, I've got a reference Django project up that I use as my template for new projects: &lt;a href="https://github.com/simplecto/django-reference-implementation" rel="noopener noreferrer"&gt;https://github.com/simplecto/django-reference-implementation&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Django commands for Asynchronous tasks
&lt;/h3&gt;

&lt;p&gt;Also, for asynchronous tasks I simply use &lt;a href="https://docs.djangoproject.com/en/3.0/howto/custom-management-commands/" rel="noopener noreferrer"&gt;custom Django commands&lt;/a&gt; which are a part of the standard framework. The pattern here is a simple &lt;code&gt;while&lt;/code&gt; loop with a &lt;code&gt;sleep()&lt;/code&gt; period. It polls the database for relevant actions and then does it's thing.  &lt;/p&gt;

&lt;p&gt;Edit: 2021 - This has paid off in a big way. I have been running a website screenshot project for over a year now with this pattern. It takes about 1500 website screenshots per day. All of it is scheduled and managed by a Django command.&lt;/p&gt;

&lt;p&gt;That project lives here: &lt;a href="https://github.com/simplecto/screenshots" rel="noopener noreferrer"&gt;https://github.com/simplecto/screenshots&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  IntercoolerJS, because who needs the complexity?
&lt;/h3&gt;

&lt;p&gt;I have a lot to say about this, but that will have to go into a series of posts. The tldr; here that I use this lovely little javascript library along with jQuery (&lt;em&gt;yeah, it is 2020, and I still use jQuery&lt;/em&gt;) to make parts of my applications &lt;em&gt;feel&lt;/em&gt; like single-page-apps but not really.&lt;/p&gt;

&lt;p&gt;IntercoolerJS keeps that old-school "Ajax" (remember that word) goodness and allows me to update the DOM with HTML from the backend. It is seamless, smooth, and really convenient for things like logins and small form updates.&lt;/p&gt;

&lt;p&gt;I strongly suggest you check it out: &lt;a href="https://intercoolerjs.org" rel="noopener noreferrer"&gt;Learn more about IntercoolerJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Edit 2021: The creator has released &lt;a href="https://htmx.org" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt;, the successor to IntercoolerJS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sentry to catch production errors
&lt;/h3&gt;

&lt;p&gt;I make mistakes. Lots of mistakes--but there is no need to show them to my users, right? Sentry gives me an easy and convenient way to capture production bugs as they happen. Some cool things about them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open source-ish (&lt;a href="https://blog.sentry.io/2019/11/06/relicensing-sentry" rel="noopener noreferrer"&gt;relicensed here&lt;/a&gt;), so you can host yourself if that is your thing.&lt;/li&gt;
&lt;li&gt;Easy few lines added to your &lt;code&gt;settings.py&lt;/code&gt; file in Django and that is it.&lt;/li&gt;
&lt;li&gt;Tight integration to your Git repos and Issue tracking systems for full production defect traceability.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Another nice thing is that you can disable it for development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sentry.io" rel="noopener noreferrer"&gt;Check out Sentry here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bitbucket Pipelines for CI/CD
&lt;/h3&gt;

&lt;p&gt;Edit: 2021 - I no longer use CI/CD for deployment of my one-man projects. It is too much tooling and complexity. I simply run tests with PyCharm and ship directly to production from my development machine.&lt;/p&gt;

&lt;p&gt;There are so many CI/CD offerings out there today, but I have been happy with Bitbucket's offering called Pipelines. They offer you a few hundred minutes free every month with the option to top-up as needed for a small fee. I have rarely had problems, and I really enjoy their YAML configuraiton / directive files.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; file I have been able to do full end-to-end testing by spinning up multiple docker containers, loading the databases, and running hundreds of test in just a few minutes. This was key in speeding things up in our team and enabling 5+ pushes to production per day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bitbucket.org" rel="noopener noreferrer"&gt;Checkout Bitbucket here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ZeroTier for VPN/Control Plane
&lt;/h3&gt;

&lt;p&gt;Finally, we come to the bit of tech that is largely optional but nice to have.  Zerotier is a unique kind of network/VPN that I use to link all my personal machines. It works though firewalls (at home, in office) and offers an easy 1 minute setup.&lt;/p&gt;

&lt;p&gt;Using ZeroTier in my last company we were able to remove the SSH Jump Servers which caused a headache in terms of key management and shared bandwidth on a single machine.&lt;/p&gt;

&lt;p&gt;Zerotier work on Linux, Mac, Windows, Android, and iPhone, so you are pretty much covered.&lt;/p&gt;

&lt;p&gt;The one downside to ZeroTier is that I don't entirely understand how it works. It is a lot like MacOS or iPhone in that it "just works" as expected and I rarely (never) have problems. That is a strength from a user experience perspective but a WTF? from a CTO perspective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Hopefully this deeper-dive will prompt some interest and curiosity about Docker, Django, Traefik, and especially IntercoolerJS. It is simple, easy to work with, and allows you to grow out of it when the time comes.&lt;/p&gt;

&lt;p&gt;Edit 2021 - As you can see not a lot has changed. I've been able to add more code to my public repositories that flesh out the ideas expressed here over a year ago.&lt;/p&gt;

</description>
      <category>code</category>
      <category>django</category>
      <category>docker</category>
    </item>
    <item>
      <title>Get More Secure Websites From Your Traefik Configuration using Mozilla's Observatory</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Tue, 24 Mar 2020 23:55:10 +0000</pubDate>
      <link>https://forem.com/simplecto/get-more-secure-websites-from-your-traefik-configuration-using-mozilla-s-observatory-46p7</link>
      <guid>https://forem.com/simplecto/get-more-secure-websites-from-your-traefik-configuration-using-mozilla-s-observatory-46p7</guid>
      <description>&lt;p&gt;Thanks to &lt;a href="https://reddit.com/u/TrollWoot"&gt;/u/TrollW00t&lt;/a&gt; on Reddit for bringing &lt;a href="https://observatory.mozilla.org/"&gt;Mozilla Observatory&lt;/a&gt; to my attention. In a previous article I talked about &lt;a href="https://dev.to/simplecto/improve-traefik-s-https-encryption-with-qualys-ssl-labs-and-testssl-sh-hda"&gt;improved  security ratings from Qualys SSL Labs&lt;/a&gt;, but I did not go far enough!&lt;/p&gt;

&lt;p&gt;My rating for simplecto.com from Mozilla Observatory was an F!&lt;/p&gt;

&lt;p&gt;The creators of Firefox certainly know a lot about browser security, so their Observatory app is a welcome tool. Here we will go step-by step and outline the changes to my config that took it from F to a B.&lt;/p&gt;

&lt;p&gt;A ratings are possible but only with Content-Security-Policy, but that can wait for another post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mozilla Observatory's 11 tests
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PQ8f3lmV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.simplecto.com/content/images/2020/03/image.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PQ8f3lmV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.simplecto.com/content/images/2020/03/image.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below are the secure headers settings I use now in my deployments:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Adding in secure headers
- traefik.http.middlewares.securedheaders.headers.forcestsheader=true
- traefik.http.middlewares.securedheaders.headers.sslRedirect=true
- traefik.http.middlewares.securedheaders.headers.STSPreload=true
- traefik.http.middlewares.securedheaders.headers.ContentTypeNosniff=true
- traefik.http.middlewares.securedheaders.headers.BrowserXssFilter=true
- traefik.http.middlewares.securedheaders.headers.STSIncludeSubdomains=true
- traefik.http.middlewares.securedheaders.headers.stsSeconds=63072000
- traefik.http.middlewares.securedheaders.headers.frameDeny=true
- traefik.http.middlewares.securedheaders.headers.browserXssFilter=true
- traefik.http.middlewares.securedheaders.headers.contentTypeNosniff=true

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



&lt;p&gt;The settings above instruct the browser to make it harder for XSS attacks, SSL downgrade attacks, and prevent the iFrame-ing of content into other pages.&lt;/p&gt;

</description>
      <category>mozillaobservatory</category>
      <category>httpsecureheaders</category>
      <category>traefik</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>Selenium Can Make Hi-DPI, Retina Style Screenshots (Firefox and Chrome)</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Tue, 17 Mar 2020 20:36:15 +0000</pubDate>
      <link>https://forem.com/heysamtexas/selenium-can-make-hi-dpi-retina-style-screenshots-firefox-and-chrome-37b9</link>
      <guid>https://forem.com/heysamtexas/selenium-can-make-hi-dpi-retina-style-screenshots-firefox-and-chrome-37b9</guid>
      <description>&lt;p&gt;With just a single line of code you can modify your Selenium browser to operate in (x.x) density mode. This means you can take higher-DPI screenshots as you might on retina screens.&lt;/p&gt;




&lt;h3&gt;
  
  
  Firefox Code Sample
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my_dpi = 2.0

profile = webdriver.FirefoxProfile()
profile.set_preference("layout.css.devPixelsPerPx", str(my_dpi))

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

&lt;/div&gt;



&lt;p&gt;Yes kids, it is this simple. All you need to do is set a preference on your profile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The gotcha here is that you have to cast the float to a string using &lt;code&gt;str()&lt;/code&gt;. That is non-obvious and will save you a ton of time in debugging. Otherwise it is silently ignored.&lt;/p&gt;




&lt;h3&gt;
  
  
  Chrome Code Sample
&lt;/h3&gt;

&lt;p&gt;Chrome is a little different. There is not a preferences profile that you tweak with various settings. In this case you simple pass in a command line option when launching the browser.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my_dpi = 2.0
options = ChromeOptions()
    # ... other options..
    options.add_argument(f"--force-device-scale-factor={my_dpi}")

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Full Code Sample
&lt;/h3&gt;

&lt;p&gt;This is an export from my Jupyter Notebook. Note, you will need to &lt;code&gt;pip install selenium&lt;/code&gt; as well as have the &lt;code&gt;firefox-geckodriver&lt;/code&gt; installed with brew or whatever linux flavor.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.common.exceptions import NoSuchElementException, WebDriverException
from time import sleep

my_dpi = 2.0

profile = webdriver.FirefoxProfile()
profile.set_preference("layout.css.devPixelsPerPx", str(my_dpi))

options = Options()
options.headless = True
driver = webdriver.Firefox(options=options, firefox_profile=profile)
driver.set_page_load_timeout(60)
driver.set_window_size(1440, 800)

driver.get('https://www.simplecto.com')

driver.save_screenshot("simplecto-dpi2.0.png")
driver.quit()

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; : This was not tested on Windows. Sorry not sorry.&lt;/p&gt;

&lt;p&gt;You can try DPI at &lt;code&gt;1.0&lt;/code&gt; and &lt;code&gt;2.0&lt;/code&gt; to see the sizable difference in disk space and memory required. This becomes a little more work for a CPU when running full-size page screenshots.&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>firefox</category>
      <category>chrome</category>
      <category>retina</category>
    </item>
    <item>
      <title>Never Trust Free Services Without an Escape Plan</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Thu, 05 Mar 2020 12:47:11 +0000</pubDate>
      <link>https://forem.com/simplecto/never-trust-free-services-without-an-escape-plan-5d90</link>
      <guid>https://forem.com/simplecto/never-trust-free-services-without-an-escape-plan-5d90</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fbeRKAkA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.simplecto.com/content/images/2020/03/never-trust-free-services-escape-plan.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fbeRKAkA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.simplecto.com/content/images/2020/03/never-trust-free-services-escape-plan.jpg" alt="Never Trust Free Services Without an Escape Plan"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I get it.&lt;/p&gt;

&lt;p&gt;You are a startup and your are scraping by, hustling, and squeezing every drop out of every lemon you can find.&lt;/p&gt;

&lt;p&gt;Or, you are just a single developer experimenting with all the free services out there.&lt;/p&gt;

&lt;p&gt;You are trying to save money at every turn until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you land that whale of a customer&lt;/li&gt;
&lt;li&gt;you land that whale of an investor&lt;/li&gt;
&lt;li&gt;you actually understand the parts of your business that make money and simply decide to put money into that&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either way, you are taking advantage of free services like Amazon's Free Tier (TM), Google's free tier, Sendgrid's Free Tier and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build with the escape plan in mind
&lt;/h2&gt;

&lt;p&gt;Whenever, where ever, however you sign up for these services – always keep a plan B in mind. This plan B can be a few things:&lt;/p&gt;

&lt;h3&gt;
  
  
  #1 Do not count on anyone's good will
&lt;/h3&gt;

&lt;p&gt;You eventually have to put your money where your services are hosted...so to speak. &lt;em&gt;Are you ready for that moment, and what will the terms be?&lt;/em&gt; If you are lucky they they will grandfather you in. Which means, "hey you were early and helped us beta test this bad boy. Keep the free tier as a thank you." I don't see that happen too often.&lt;/p&gt;

&lt;p&gt;Next, you have the option to move to a paid plan with a discount. This might suit you, it might not. But we are cheap bastards, and that $8/month is a real pinch.&lt;/p&gt;

&lt;p&gt;So now that leaves you with the final option. Pay the price or GTFO (Get the f*ck out). Fair enough, freeloaders cost money, and everyone has to eat.&lt;/p&gt;

&lt;h3&gt;
  
  
  #2 Set aside some cash for when it time to pay the devil
&lt;/h3&gt;

&lt;p&gt;You can simply start paying and keep your infrastructure/services in place without disruption. (This is the grown-up thing to do) It is also a good idea if you buy-in to propreitary serivices offered by the likes of cloud providers like AWS, Azure, and CGP. Cloud providers are all the devil, and this is the very deal they want you to make.&lt;/p&gt;

&lt;p&gt;No evil here, but know the deal.&lt;/p&gt;

&lt;h3&gt;
  
  
  #3 Only procure services that are built on open source
&lt;/h3&gt;

&lt;p&gt;Many companies run an open source business model which means that you can run their software in your own infrastrucure at zero-cost (no counting the cost of your time or hosting). This can be a win, especially in regulated environments like healthcare and banking.&lt;/p&gt;

&lt;p&gt;It also frees you from having to scramble to find a comparable or compatible service to the existing features you get for free now. Sentry.io is a great example of this model. They offer a compelling paid product but also offer the same experience and software as a download. Should you need to move away from the hosted version it is very easy to host yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  #4 Only procure with open standards
&lt;/h3&gt;

&lt;p&gt;This escape plan there also applies to service like SMTP (email sending). You can move from Mailgun to sendgrid to your own with some DNS and API key changes. No big deal.&lt;/p&gt;

&lt;p&gt;This also applies to the likes of container hosting. Across the board the providers offer some kinds of Kubernetes or Docker support. Keeping your deployments and infrastructure within those environments goes a long way to ensure that you are not too locked in to that provider should the tables turn.&lt;/p&gt;

&lt;h3&gt;
  
  
  #5 Think ahead, have the fire-drill in place
&lt;/h3&gt;

&lt;p&gt;Build and deploy absolutely &lt;strong&gt;knowing&lt;/strong&gt; this is going to happen. When it does, then you already know what needs doing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adjust the dns&lt;/li&gt;
&lt;li&gt;swap the api keys&lt;/li&gt;
&lt;li&gt;generate new certs&lt;/li&gt;
&lt;li&gt;etc, etc, etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then execute! If you are lucky then they providers will give you plenty of time to plan and allocate resources to roll over. In case they don't then it does not hurt to have this punch list in place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Don't be surprised when the free is no longer free. Be prepared either with cash in hand or a solid plan to pull up stakes and leave.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>strategy</category>
      <category>startup</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>Let's Go Shopping on Github, or 5 Tips to Pick the Right Software</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Wed, 26 Feb 2020 08:35:00 +0000</pubDate>
      <link>https://forem.com/simplecto/let-s-go-shopping-on-github-or-5-tips-to-pick-the-right-software-1ke2</link>
      <guid>https://forem.com/simplecto/let-s-go-shopping-on-github-or-5-tips-to-pick-the-right-software-1ke2</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RZ3-uO8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.simplecto.com/content/images/2020/02/stem-barbie.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RZ3-uO8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.simplecto.com/content/images/2020/02/stem-barbie.jpg" alt="Let's Go Shopping on Github, or 5 Tips to Pick the Right Software"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are literally thousands of open-source projects posted in GitHub. Depending on your use-cases, there may be tens if not more projects which deliver functionality to your project or company.&lt;/p&gt;

&lt;p&gt;How, then, can you be sure to pick the right ones?&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the License
&lt;/h3&gt;

&lt;p&gt;Check the kind of open-source license (if any) this project is using. It might not be the best choice for you depending on your cases. Learn more about choosing projects with compatible licenses here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tldrlegal.com/"&gt;https://tldrlegal.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This can become a complex topic depending on your Legal situation. That said, during a due diligence (pre-investment, acquisition, or vendor) you will face questions about what software you use and what licenses you are subject to).&lt;/p&gt;

&lt;h3&gt;
  
  
  Check compatibility with your stack
&lt;/h3&gt;

&lt;p&gt;Make sure that the code is compatible with your current stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supported OS&lt;/li&gt;
&lt;li&gt;Language version support (Eg- python 2.7 vs 3.x, or Swift 2,3,4, PHP 5.x, 7.x, etc)&lt;/li&gt;
&lt;li&gt;Will it work with your chosen database (if required).&lt;/li&gt;
&lt;li&gt;Browser support&lt;/li&gt;
&lt;li&gt;Hardware and system requirements (Eg- do you have enough cores, ram, and disk space?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: this library may have it’s own dependencies, which triggers its of list of compatibility requirements. This is why it could be best to use a dependency manager like Pip, Composer, NPM, Maven, or whatever depending on your chosen language.&lt;/p&gt;

&lt;p&gt;Another issue for Python users out there: Python2 vs Python3 – it is 2020 and there are still some dependencies stuck on v2. Further complicating things could be that some libraries will only work in Python3 or 3.6 or greater. Beware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the health of the project
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Is the project alive, on life-support, or in the graveyard?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is probably the most important metric I use when picking. Please note, this is subjective. Over time you will develop a “feel” for this kind of thing and set your own limits.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Number of stars indicate interest from the broader community&lt;/li&gt;
&lt;li&gt;Number of open issues indicate engaged developers and users&lt;/li&gt;
&lt;li&gt;Number of closed issues indicate some kind of resolution processes are in place (eg — bugs are getting fixed and features delivered)&lt;/li&gt;
&lt;li&gt;Check the documentation. Is it up to date with the current code base?&lt;/li&gt;
&lt;li&gt;Date of the last public release&lt;/li&gt;
&lt;li&gt;Is the project under stewardship (eg - Apache incubated) or is it simply sponsored by a company or single developer (aka &lt;a href="https://en.wikipedia.org/wiki/Benevolent_dictator_for_life"&gt;BDFL&lt;/a&gt;)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Caveat: This generally holds true for me, but it is quite possible that some projects are considered stable and will not need updates for long periods of time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does it trigger any new hardware or infrastructure dependencies?
&lt;/h3&gt;

&lt;p&gt;It might require deploying new backing services such as storage, caching, or authentication services. This can significantly increase your complexity or costs of the project.&lt;/p&gt;

&lt;p&gt;One such anecdote is that we wanted a thumbnail-generating library for a website project. It just so happens that managing thumnails at scale brings significant speed, memory, and caching challenges. The chosen library offered more functionality but at the cost of adding caching services. This was not budgetd for (mentally or financially), and the library was scrapped.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the security risks?
&lt;/h3&gt;

&lt;p&gt;Check the &lt;a href="https://cve.mitre.org/"&gt;CVE&lt;/a&gt; and &lt;a href="https://nvd.nist.gov/vuln/search"&gt;NVS&lt;/a&gt; databases for any unresolved security issues. This will have major implications of you maintain risk assessments, compliance records, or security audits.&lt;/p&gt;

</description>
      <category>code</category>
      <category>bestpractices</category>
      <category>protip</category>
      <category>wisdom</category>
    </item>
    <item>
      <title>Use Traefik 2 with Nginx, Apache, or CaddyServer to Serve Static Files</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Mon, 24 Feb 2020 06:09:00 +0000</pubDate>
      <link>https://forem.com/heysamtexas/use-traefik-2-with-nginx-apache-or-caddyserver-to-serve-static-files-3b4l</link>
      <guid>https://forem.com/heysamtexas/use-traefik-2-with-nginx-apache-or-caddyserver-to-serve-static-files-3b4l</guid>
      <description>&lt;p&gt;Setup a simple HTTP static file server behind Traefik. My specific use case is to serve static files (eg - PDF, excel, etc) from my blog that runs &lt;a href="https://ghost.org" rel="noopener noreferrer"&gt;Ghost&lt;/a&gt;. However, the software itself does not support that. There are a number of HTTP servers, but we will choose three of the most popular ones.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nginx.com/" rel="noopener noreferrer"&gt;Nginx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.caddyserver.com" rel="noopener noreferrer"&gt;CaddyServer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://httpd.apache.org/" rel="noopener noreferrer"&gt;Apache HTTPD&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These work well because they provide a nice drop-in to solve static file serving because it is small, fast, and will work well in a default configuration.&lt;/p&gt;

&lt;p&gt;Now we will explore two ways to serve up files.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hosting on a subdomain (eg - &lt;code&gt;files.example.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Hosting on the blog's domain but in a subfolder (eg - &lt;code&gt;example.com/static&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use &lt;code&gt;docker-compose&lt;/code&gt; and docker labels to configure Traefik's routing and middlewares.&lt;/p&gt;

&lt;p&gt;In both scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the images from Docker Hub.&lt;/li&gt;
&lt;li&gt;Bind-mount a local folder to host the files for the web server. See &lt;code&gt;volumes:&lt;/code&gt; in the docker-compose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  On its own domain
&lt;/h2&gt;

&lt;p&gt;You must have the DNS entry, &lt;code&gt;files.example.com&lt;/code&gt; in this case, to point at the server. An example file download URL would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://files.example.com/sales-pitch.pdf" rel="noopener noreferrer"&gt;https://files.example.com/sales-pitch.pdf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  All-in-one config (docker-compose)
&lt;/h3&gt;

&lt;p&gt;This is a &lt;code&gt;docker-compose.yml&lt;/code&gt; with configurations for all three servers. Simply comment out the ones you don't want and un-comment the ones you do. Bobs- your uncle.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    static:

        # nginx config
        image: nginx
        volumes:
            - ./files:/usr/share/nginx/html:ro

# uncomment to use Aapche HTTPD Server
# image: httpd:2.4-alpine
# volumes:
# - ./files:/usr/local/apache2/htdocs:ro

# Uncomment to use Caddeserver
# image: caddy/caddy:scratch
# command: file-server --root /files --listen 0.0.0.0:80
# volumes:
# - ./files:/files:ro

        container_name: static-files
        restart: unless-stopped
        networks:
            - traefik
        labels:
            # Match on the hostname and the path
            - traefik.http.routers.nginx.rule=Host(`files.example.com`)
            - traefik.http.routers.nginx.tls=true
            - traefik.http.routers.nginx.tls.certresolver=le
            - traefik.http.services.nginx.loadbalancer.server.port=80

            # tell Traefik which middlewares we want to use on this container
            - traefik.http.routers.nginx.middlewares=gzip

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  On a specific URI path (/static) in an existing domain
&lt;/h2&gt;

&lt;p&gt;It is only a matter of three lines that will enable this to live under &lt;code&gt;/static&lt;/code&gt; Path. An example file download URL would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.example.com/static/sales-pitch.pdf" rel="noopener noreferrer"&gt;https://blog.example.com/static/sales-pitch.pdf&lt;/a&gt;
&amp;lt;!--kg-card-begin: markdown--&amp;gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Just the parts you need&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;# Match on the hostname and the path
- traefik.http.routers.nginx.rule=(Host(`blog.example.com`) &amp;amp;&amp;amp; Path(`/static`))

# Define a new middleware to strip the URL prefix before sending it to nginx
- traefik.http.middlewares.nginx-stripprefix.stripprefix.prefixes=/static

# tell Traefik which middlewares we want to use on this container
- traefik.http.routers.nginx.middlewares=gzip@docker,nginx-stripprefix@docker

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

&lt;/div&gt;



&lt;p&gt;&amp;lt;!--kg-card-end: markdown--&amp;gt;&amp;lt;!--kg-card-begin: markdown--&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full docker-compose.yml&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;    static:

        # nginx config
        image: nginx
        volumes:
            - ./files:/usr/share/nginx/html:ro

# uncomment to use Aapche HTTPD Server
# image: httpd:2.4-alpine
# volumes:
# - ./files:/usr/local/apache2/htdocs:ro

# Uncomment to use Caddeserver
# image: caddy/caddy:scratch
# command: file-server --root /files --listen 0.0.0.0:80
# volumes:
# - ./files:/files:ro

        container_name: static-files
        restart: unless-stopped
        networks:
            - traefik
        labels:
            # Match on the hostname and the path
            - traefik.http.routers.static-files.rule=(Host(`blog.example.com`) &amp;amp;&amp;amp; Path(`/static`))
            - traefik.http.routers.static-files.tls=true
            - traefik.http.routers.static-files.tls.certresolver=le
            - traefik.http.services.static-files.loadbalancer.server.port=80

            # Define a new middleware to strip the URL prefix before sending it to static-files
            - traefik.http.middlewares.static-files-stripprefix.stripprefix.prefixes=/static

            # tell Traefik which middlewares we want to use on this container
            - traefik.http.routers.static-files.middlewares=gzip@docker,static-files-stripprefix@docker

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Things to note
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The CaddyServer config needed a &lt;code&gt;command:&lt;/code&gt; attribute added to tell it to activate its fileserver feature.&lt;/li&gt;
&lt;li&gt;The mountpoints for &lt;code&gt;volumes:&lt;/code&gt; is different per server&lt;/li&gt;
&lt;li&gt;Only official images are used. Your milage may vary if you stray from these...&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Other guides
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I previously made a guide on hosting your files using a nice Golang service called filebrowser. &lt;a href="https://dev.to/simplecto/ghost-file-downloads-with-traefik-docker-and-filebrowser-11pm"&gt;Read more about Ghost and Filebrowse here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Using the above snippets you should be able to add Nginx, Apache HTTPD, or CaddyServer to your docker / Traefik deployments and have simple, no fuss static file serving.&lt;/p&gt;

&lt;p&gt;I should also mention that is snipped is part of my production stack template that use for most Simple CTO projects. More on that later&lt;/p&gt;

</description>
      <category>code</category>
      <category>nginx</category>
      <category>traefik</category>
      <category>caddyserver</category>
    </item>
    <item>
      <title>3 Things to Do Before DDoS'ing Yourself with a CDN/Application Gateway</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Mon, 17 Feb 2020 12:25:48 +0000</pubDate>
      <link>https://forem.com/heysamtexas/3-things-to-do-before-ddos-ing-yourself-with-a-cdn-application-gateway-18pl</link>
      <guid>https://forem.com/heysamtexas/3-things-to-do-before-ddos-ing-yourself-with-a-cdn-application-gateway-18pl</guid>
      <description>&lt;p&gt;Every now and then I get back into the Azure ecosystem to see what is there. I recently came across their &lt;a href="https://azure.microsoft.com/en-us/services/frontdoor/" rel="noopener noreferrer"&gt;Front Door&lt;/a&gt; product which looked like competitor to &lt;a href="https://cloudflare.com" rel="noopener noreferrer"&gt;CloudFlare&lt;/a&gt; or a rehash of their &lt;a href="https://azure.microsoft.com/en-us/services/application-gateway/" rel="noopener noreferrer"&gt;Application Gateway&lt;/a&gt;. To be honest there are so many products that do so many things it all kind of blends together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is not a post about Front Door.&lt;/strong&gt; It only plays a supporting role in this little story of how many DDoS events are often self-imposed.&lt;/p&gt;

&lt;p&gt;Anyway, I thought the Front Door product looked interesting, so begin to setup a test against my little &lt;a href="https://screenshot.simplecto.com/?ref=ddosed-myself" rel="noopener noreferrer"&gt;screenshot side project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It takes a few minutes to deploy a Front Door, but once it was up I tried it out.&lt;/p&gt;

&lt;p&gt;504 Gateway errors...&lt;/p&gt;

&lt;p&gt;Uh-oh.&lt;/p&gt;

&lt;p&gt;I head into the weblogs on Traefik and see that I'm getting hammered from a number of IPs in the same B-Class subnet (147.xx.00.00/16). It occurs to me that the Front Door edge servers are attempting to pull content from the site, but this little VM and application cannot keep up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason 1 - No caching (Appliction architecture)
&lt;/h3&gt;

&lt;p&gt;There is no caching in the application. Traefik does not bother with caching, so the issue is in my little Django app. There is no caching configured, nor does it calculate ETags or offer any kind of 304 responses. That means every page request is it hammering Postgres, fetching the data, and building the pages. This should not be a big problem, except that I'm storing images as bytes in the database (because reasons). That means pushing more data than expected from one service to django, assembling the page, and sending it back. Oops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitigation:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.djangoproject.com/en/3.0/topics/cache/#the-per-view-cache" rel="noopener noreferrer"&gt;Add in-memory page caching for home page&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.djangoproject.com/en/3.0/topics/conditional-view-processing/#the-condition-decorator" rel="noopener noreferrer"&gt;Added 304 calculation based on most recently added screenshot&lt;/a&gt;. This is called conditional-view processing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Reason 2 - No rate-limiting on the Reverse Proxy (Traefik)
&lt;/h3&gt;

&lt;p&gt;A quick calculation showed that the Front Door was attempting to make about 25 requests per second in an attempt to acquire the content. I could configure Traefik to rate limit based on IP, subnet, and a few other factors as well. Handing back errors to the CDN is not ideal. However, rate-limiting in general is not a bad idea.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitigation&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.traefik.io/middlewares/ratelimit/" rel="noopener noreferrer"&gt;Add rate-limiting for reasonable human usage of the site&lt;/a&gt;. (eg - about 15 reqs/second, including all loading of assets)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Reason 3 - The CDN was set to aggressively health check improper endpoint
&lt;/h3&gt;

&lt;p&gt;As you can imagine getting the CDN configured is also tricky. I am first to admit that I don't know what I'm doing all the time. Upon looking into it I could see that the CDN was simply doing HEAD requests on the home page, which caused quite a load on my little server. The smarter thing to do here would have been to offer up an endpoint that simply return 200OK (or something that did not load my systems too heavily). This became too much when 40+ edge servers are requesting the same data every 30 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitigation&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a &lt;strong&gt;/health-check&lt;/strong&gt; endpoint to only return 200OK on HEAD and GET requests. Configure Front Door to hit that instead&lt;/li&gt;
&lt;li&gt;Decrease frequency of healthcheck from every 30 seconds to 300 seconds.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;A CDN/App gateway by itself can do more harm than good. It is not a turn-key operation, and you should be prepared before kicking off a project like that. Consider making a risk/gap analysis and subsequent checklist before undertaking such a project. My checklist follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make sure the app offers a light-weight health check endpoint&lt;/li&gt;
&lt;li&gt;Make sure the CDN hits that health-check endpoint&lt;/li&gt;
&lt;li&gt;Setup sane rate-limiting on your reverse proxy and provide helpful gateway errors for when people see it. (Reddit does a pretty good job here)&lt;/li&gt;
&lt;li&gt;Measure the performance of your site before / after the implementation. Was it worth all the trouble? Could you have just bought a bigger server for the same benefit and fewer moving parts?&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ddos</category>
      <category>traefik</category>
      <category>docker</category>
      <category>code</category>
    </item>
    <item>
      <title>Improve Traefik's HTTPS Encryption with Qualys SSL Labs and testssl.sh</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Fri, 14 Feb 2020 12:18:13 +0000</pubDate>
      <link>https://forem.com/heysamtexas/improve-traefik-s-https-encryption-with-qualys-ssl-labs-and-testssl-sh-hda</link>
      <guid>https://forem.com/heysamtexas/improve-traefik-s-https-encryption-with-qualys-ssl-labs-and-testssl-sh-hda</guid>
      <description>&lt;p&gt;&lt;strong&gt;tldr;&lt;/strong&gt; Encryption (and HTTPS) is a complicated beast, but we have to do our best to make sure that our sites run securely. With the help of tools like &lt;strong&gt;Qualys SSL Labs [1]&lt;/strong&gt; or the open-source &lt;strong&gt;testssl.sh [2]&lt;/strong&gt; I update my production Traefik installations to run with the most secure configurations as possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt; : I am not an encryption expert and will be the first to admit that there is a bit of "cargo culting." I am making the changes necessary to my configurations such that the SSL labs gives an "A" grade or better, and that I see no RED flags from testssl.sh. I provide no advice here but rather share the configuration and outcomes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before we begin
&lt;/h3&gt;

&lt;p&gt;Before you start disabling old encryption protocial it is a good idea to know who your users are and what software stacks they use to connect with you. Older stacks such as IE11 on older Windows or Safari on older Macs may break. See all the red? Yeah...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Fimage-22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Fimage-22.png" width="800" height="400"&gt;&lt;/a&gt;SSL Labs report (after changes)&lt;/p&gt;

&lt;h3&gt;
  
  
  My current config
&lt;/h3&gt;

&lt;p&gt;Below you see the "B" rating for the SSL configuration for simplecto.com. This is in part because the current configuration allows for older, less secure versions of TLS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Fimage-19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Fimage-19.png" width="800" height="400"&gt;&lt;/a&gt;Old rating for simplecto.com on SSL Labs&lt;/p&gt;

&lt;h3&gt;
  
  
  My current config that earned the "B" rating
&lt;/h3&gt;

&lt;p&gt;This is my current config. There are no options set for encryption settings, cyphers, protocols, etc. I simply take the defaults from Traefik. This may or may not be sufficient for you to pass a security or compliance audit.&lt;/p&gt;

&lt;p&gt;I use a single &lt;code&gt;docker-compose.yml&lt;/code&gt; file to deploy which keeps complexity to a minimum.&lt;/p&gt;



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

services:

    traefik:
        container_name: traefik
        image: traefik:v2.1
        command:
            - "--providers.docker=true"
            - "--entryPoints.web.address=:80"
            - "--entryPoints.websecure.address=:443"
            - "--certificatesResolvers.le.acme.email=secret_email@domain.com"
            - "--certificatesResolvers.le.acme.storage=acme.json"
            - "--certificatesResolvers.le.acme.tlsChallenge=true"
            - "--certificatesResolvers.le.acme.httpChallenge=true"
            - "--certificatesResolvers.le.acme.httpChallenge.entryPoint=web"

        restart: always
        ports:
            - 80:80
            - 443:443
            - 8080:8080
        networks:
            - web
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock
            - ./acme.json:/acme.json
        labels:

            # Redirect all HTTP to HTTPS permanently
            - traefik.http.routers.http_catchall.rule=HostRegexp(`{any:.+}`)
            - traefik.http.routers.http_catchall.entrypoints=web
            - traefik.http.routers.http_catchall.middlewares=https_redirect
            - traefik.http.middlewares.https_redirect.redirectscheme.scheme=https
            - traefik.http.middlewares.https_redirect.redirectscheme.permanent=true

networks:
    web:
        external: true

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  What needs to change to get the "A"?
&lt;/h3&gt;

&lt;p&gt;I went searching for answers and quickly found myself on the &lt;strong&gt;Containous community forums [3]&lt;/strong&gt;. It was a post there that held the answers.&lt;/p&gt;

&lt;p&gt;I must tell Traefik that I want TLS 1.3 (still experimental) and that the minimum version to support is TLSv1.2. Oh, and remember that pristine single-file config? Yeah, well, now I need to bind-mount some static files into the container. No biggie, but slightly more complex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The new traefik_conf.yaml&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;yaml&lt;/code&gt; file tells Traefik to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make the minimum supported version of TLS 1.2 and to enable 1.3. &lt;/li&gt;
&lt;li&gt;Only accept connections where the hostname is specified (SNI)&lt;/li&gt;
&lt;li&gt;Support a specific list of Cyphers (the cargo-cult part for me)
&amp;lt;!--kg-card-begin: markdown--&amp;gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tls:
  options:
    default:
      minVersion: VersionTLS12
      sniStrict : true
      cipherSuites:
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305

    mintls13:
      minVersion: VersionTLS13

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

&lt;/div&gt;



&lt;p&gt;Now that we have this, we need to get it into the container and tell Traefik to use it.&lt;/p&gt;

&lt;p&gt;There are two lines we need to add to our &lt;code&gt;docker-compose.yml&lt;/code&gt; file:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;"--providers.file.filename=/traefik_conf.yaml"&lt;/code&gt; under &lt;code&gt;command:&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./traefik_conf.yaml:/traefik_conf.yaml&lt;/code&gt; under &lt;code&gt;volumes:&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Fimage-20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Fimage-20.png" width="800" height="400"&gt;&lt;/a&gt;The two lines you need (in context of the docker-compose.yml)&lt;/p&gt;

&lt;p&gt;Now all we need to do is &lt;code&gt;docker-compose up -d&lt;/code&gt; and re-test!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Fimage-21.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Fimage-21.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wohoo! – Ok, A-grade achieved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test locally with testssl.sh
&lt;/h3&gt;

&lt;p&gt;As an alternative to SSL Labs there is a nice open-source tool called testssl.sh. You can run this locally and see way more information that you might know what to do with. You can test on any docker-enabled machine with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker run --rm -ti drwetter/testssl.sh example.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There is quite a bit of output, but suffice to say you will get more than you are looking for :-)&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;With a small configuration change to Traefik we were able to move our SSL Labs overall rating from B to A. We had to add support for TLS1.3 and remove support for TLS 1.0 and 1.1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html" rel="noopener noreferrer"&gt;https://www.ssllabs.com/ssltest/analyze.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/drwetter/testssl.sh" rel="noopener noreferrer"&gt;https://github.com/drwetter/testssl.sh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.containo.us/t/poor-rating-on-ssl-labs/2400/7" rel="noopener noreferrer"&gt;https://community.containo.us/t/poor-rating-on-ssl-labs/2400/7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.traefik.io/v2.0/providers/file/#filename" rel="noopener noreferrer"&gt;https://docs.traefik.io/v2.0/providers/file/#filename&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>traefik</category>
      <category>code</category>
      <category>letsencrypt</category>
      <category>devops</category>
    </item>
    <item>
      <title>Building my own Screenshot-as-a Service with Docker, Django, and Selenium</title>
      <dc:creator>Sam Texas</dc:creator>
      <pubDate>Thu, 13 Feb 2020 08:47:00 +0000</pubDate>
      <link>https://forem.com/heysamtexas/building-my-own-screenshot-as-a-service-with-docker-django-and-selenium-41g2</link>
      <guid>https://forem.com/heysamtexas/building-my-own-screenshot-as-a-service-with-docker-django-and-selenium-41g2</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Ffamous-last-words-2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2Ffamous-last-words-2.jpg" alt="Building my own Screenshot-as-a Service with Docker, Django, and Selenium" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tldr;&lt;/strong&gt; There are no shortage of API or services for getting website screenshots. However, this is a good experiment to explore what it takes to make high-fidelity screenshots. Let me be clear--this is largely a "solved problem" as demonstrated by multiple companies in the space (see appendix). There are even some open-source projects solving pieces of it at a time.&lt;/p&gt;

&lt;p&gt;This distraction satisfies a years-long curiosity I have had about making website screenshots in an automated way.&lt;/p&gt;

&lt;h3&gt;
  
  
  tldr part2; Show me the code!
&lt;/h3&gt;

&lt;p&gt;Ok, you pushy bastards. The whole Django project is here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/simplecto/screenshots" rel="noopener noreferrer"&gt;https://github.com/simplecto/screenshots&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Some thoughts and guiding principles of this project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Simplicity (hah, I know there are &lt;a href="https://en.wiktionary.org/wiki/yak_shaving" rel="noopener noreferrer"&gt;Yaks that need shaving&lt;/a&gt;), meaning no CI/CD, no Kubernetes, deploy to prod right from dev machine, and other "sins."&lt;/li&gt;
&lt;li&gt;How does execution look when I am not at all concerned with scale of any kind?&lt;/li&gt;
&lt;li&gt;Optimize only for developer productivity?&lt;/li&gt;
&lt;li&gt;Slow your roll and document things (check that README.md, yo)&lt;/li&gt;
&lt;li&gt;Challenge &lt;strong&gt;&lt;em&gt;old truths&lt;/em&gt;&lt;/strong&gt; like, "storing images in the database is bad" and "using a database as a queue is bad."&lt;/li&gt;
&lt;li&gt;How far can we push cheap, commodity VMs at a place like &lt;a href="https://www.scaleway.com" rel="noopener noreferrer"&gt;Scaleway&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;How far can this single server scale vertically before needing a 2nd server?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  System Architecture
&lt;/h3&gt;

&lt;p&gt;Below is the current system architecture used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2FSystem-architecture-1-.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.simplecto.com%2Fcontent%2Fimages%2F2020%2F02%2FSystem-architecture-1-.jpg" alt="Building my own Screenshot-as-a Service with Docker, Django, and Selenium" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case you missed it, most of my projects (personal and professional) follow a similar pattern of system and application architecture: &lt;a href="https://dev.to/simplecto/docker-django-traefik-and-intercoolerjs-is-my-go-to-stack-for-building-a-saas-in-2020-pdf"&gt;linked here&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Django for web&lt;/li&gt;
&lt;li&gt;Django Commands for async tasks&lt;/li&gt;
&lt;li&gt;Postgresql&lt;/li&gt;
&lt;li&gt;Traefik for reverse proxy and HTTPS &lt;/li&gt;
&lt;li&gt;All on a single Ubuntu Linux VM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scale is not an issue
&lt;/h3&gt;

&lt;p&gt;I always preface this with "scale is not an issue" when starting new projects. You have no customers, no users--nothing! So, it makes sense that we should not distract ourselves with the complexity of distributed systems, remote storages, or other things.&lt;/p&gt;

&lt;p&gt;Should we need them we can revisit this conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple deployment
&lt;/h3&gt;

&lt;p&gt;Everything you see lives on a single system all running in plain old Docker. I use a &lt;code&gt;docker-compose.yml&lt;/code&gt; file to keep deployment simple.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about my setup for &lt;a href="https://dev.to/simplecto/my-configuration-for-traefik-2-0-docker-and-letsencrypt-285d"&gt;reverse proxy configuration with LetsEncrypt and Traefik&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  If you can Docker and Django then you can Screenshot
&lt;/h3&gt;

&lt;p&gt;That may already sound technically heavy, but it is nothing compared to the massive stacks of AWS Lambda, DynamoDB, Cloud Front, S3, and Cloud Formation whatever just to take pictures of web pages. My goal is to spin up a dev environment in a few minutes without any need for third-party apis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are old truths still truths?
&lt;/h3&gt;

&lt;p&gt;My reasononing is that what was true in 2010 is not necessarily true in 2020. Compute, bandwidth, and storage are far cheaper and plentiful. For example, 1TB of data today is not that much from a capacity perspective. However, that is potentially 10 million images at 100Kb each. (Screenshots, yo).&lt;/p&gt;

&lt;p&gt;Developer time is far more expensive than CPU, Bandwidth, or Storage. Why then do we let, or even encourage developers, to spend so much time on optimizations when it is more efficient to buy a bigger server?&lt;/p&gt;

&lt;h3&gt;
  
  
  Appendix: Alternative Screenshot Service Providers
&lt;/h3&gt;

&lt;p&gt;See? I was not kidding. This list is current as of February 2020.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloudbrowser.co/products" rel="noopener noreferrer"&gt;CloudBrowser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.geoscreenshot.com/" rel="noopener noreferrer"&gt;GeoScreenshot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.lambdatest.com/" rel="noopener noreferrer"&gt;LambdaTest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Page2Images&lt;/li&gt;
&lt;li&gt;PagePeeker API&lt;/li&gt;
&lt;li&gt;PageScreen&lt;/li&gt;
&lt;li&gt;PageScreenShot&lt;/li&gt;
&lt;li&gt;Screen.rip&lt;/li&gt;
&lt;li&gt;Screenshot Bin API&lt;/li&gt;
&lt;li&gt;ScreenshotAPI.net&lt;/li&gt;
&lt;li&gt;ScreenshotsCloud API&lt;/li&gt;
&lt;li&gt;Thumbalizr&lt;/li&gt;
&lt;li&gt;WebCargo&lt;/li&gt;
&lt;li&gt;WebShrinker&lt;/li&gt;
&lt;li&gt;WhoAPI&lt;/li&gt;
&lt;li&gt;apercite.fr&lt;/li&gt;
&lt;li&gt;browserstack.com&lt;/li&gt;
&lt;li&gt;browshot.com&lt;/li&gt;
&lt;li&gt;grabz.it&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apiflash.com/" rel="noopener noreferrer"&gt;apiflash.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://capture.techulus.in/" rel="noopener noreferrer"&gt;capture.techulus.in/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://screenshot-v2.now.sh/" rel="noopener noreferrer"&gt;https://screenshot-v2.now.sh/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;linkpeek.com&lt;/li&gt;
&lt;li&gt;pagelr.com&lt;/li&gt;
&lt;li&gt;paste.pics&lt;/li&gt;
&lt;li&gt;restpack.io/screenshot&lt;/li&gt;
&lt;li&gt;screencloud.net&lt;/li&gt;
&lt;li&gt;screenshotlayer.com&lt;/li&gt;
&lt;li&gt;screenshotmachine.com&lt;/li&gt;
&lt;li&gt;shrinktheweb.com&lt;/li&gt;
&lt;li&gt;site-shot.com&lt;/li&gt;
&lt;li&gt;snapito.com&lt;/li&gt;
&lt;li&gt;snipboard.io&lt;/li&gt;
&lt;li&gt;stillio.com&lt;/li&gt;
&lt;li&gt;thum.io&lt;/li&gt;
&lt;li&gt;thumbnail.ws API&lt;/li&gt;
&lt;li&gt;web-capture.net&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://screenshot.simplecto.com" rel="noopener noreferrer"&gt;TRY IT!! screenshot.simplecto.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>django</category>
      <category>saas</category>
      <category>code</category>
    </item>
  </channel>
</rss>
