<?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: Mike</title>
    <description>The latest articles on Forem by Mike (@restlessmike).</description>
    <link>https://forem.com/restlessmike</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%2F3269017%2F61bfc4e4-1ddd-4f11-8e0b-ff2b7b1384a2.jpg</url>
      <title>Forem: Mike</title>
      <link>https://forem.com/restlessmike</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/restlessmike"/>
    <language>en</language>
    <item>
      <title>Some Thoughts on Pricing</title>
      <dc:creator>Mike</dc:creator>
      <pubDate>Tue, 18 Nov 2025 14:59:33 +0000</pubDate>
      <link>https://forem.com/restlessmike/some-thoughts-on-pricing-4c1m</link>
      <guid>https://forem.com/restlessmike/some-thoughts-on-pricing-4c1m</guid>
      <description>&lt;p&gt;When putting together RestlessIDE (the soon-to-be &lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;best web-based development environment&lt;/a&gt;!) I had a lot of different thoughts floating around in my head. I have been a huge fan of web-based development for a long time. Back in the 2016-2017 timeframe I used an Acer Chromebox as my main dev setup, with Cloud9 IDE being my daily driver. It had its quirks but was generally a solid experience. Then Amazon bought and killed it for some reason.&lt;/p&gt;

&lt;p&gt;I then tried switching to one of its competitors (they’re still around, so I won’t name names. YET!) and the experience was more mixed. The editor itself was mostly fine, but the terminals tended to lose connectivity regularly; you’d see text but couldn’t type, and would need to reload the page and hope it worked out. Things generally felt just a little bit &lt;em&gt;off.&lt;/em&gt;  Shortly thereafter, I retreated back to Linux and VS Code, thinking my dream of permanently-online editors was done.&lt;/p&gt;

&lt;p&gt;After a few years, I started to get that itch again. Poking around I saw the scene had changed quite a bit. I logged on to the one I had been using previously and found that they had actually bifurcated their product, with the one I had been using (and paying for) now the “legacy” version with a whole separate login and a worse overall experience since they hadn’t been putting much time into improving it. The new version did have a nicer UI, but it had become infected by a disease I call &lt;strong&gt;Cloud Pricing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The more I looked around, I saw the same thing with pretty much every other service. Let me explain.&lt;/p&gt;

&lt;h2&gt;
  
  
  To the Cloud!
&lt;/h2&gt;

&lt;p&gt;An interesting thing happened back in 2006: Amazon opened up Amazon Web Services (or AWS) to the outside world. While not the first service to offer the hourly pricing model, it quickly took the developer world by storm just due to Amazon’s scale. Now every two-bit startup in the world could get servers with a simple API call and scale their infrastructure as needed to meet any level of customer usage. Just starting out? Get one EC2 instance, maybe a micro. Get more traffic? Move up to a small. Hit the front page of Digg? Let’s load balance some 2XLs!&lt;/p&gt;

&lt;p&gt;Over the years this hourly pricing model became very popular among other companies in the infrastructure space. First of all, many of them were built on AWS, so they needed to pass along those costs somehow. And second, it gave an easy point of comparison to people trying to shop prices. And saying you cost $0.119 per hour sounds a heck of a lot better  $85.68 per month.&lt;/p&gt;

&lt;p&gt;The problem with this model is that it is &lt;strong&gt;metered&lt;/strong&gt;. You pay for usage (which is fair, to a point) but you also never know at the beginning of each month what you’ll be paying in the end. And if you’re anything like me, the surprises are not often in your favor. The metered approach also has led to “usage” being redefined in ways that don’t benefit consumers. Once you’re committed, you’re basically forced to pay whatever the service provider says, or your infrastructure goes away.&lt;/p&gt;

&lt;p&gt;Using AWS as an example, they started off with a handful of services. Two of the earlier ones were Simple Storage Service (S3) and Elastic Compute Cloud (EC2). The former let you upload files into buckets and have them available wherever you need, either privately or publicly with convenient download URLs. The latter let you launch virtual machines of different specs for different hourly rates. The math was relatively simple for each. On S3 you pay a certain amount per GB per month, and with EC2 you pay for the time that the server is running. Light bulbs went off and people did the math, working those costs into their own business models.&lt;/p&gt;

&lt;p&gt;Then those costs began to creep up.&lt;/p&gt;

&lt;p&gt;It turns out, S3 also charges a bit for data leaving Amazon’s network. Just a little bit, as long as your public link doesn’t get hit by a botnet for some reason and downloaded 1,000,000 times. And EC2 was expanded and grew into a whole constellation of add-on services, each with their own charges. To store data permanently, you would use Elastic Block Store (EBS) volumes. ($) To manage splitting traffic between multiple EC2 servers you’d use an Elastic Load Balancer. ($$) If you want to host a static site in S3 and need a certificate (who uses naked HTTP these days?) you’ll need to put it behind CloudFront ($$$).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Of course&lt;/em&gt; it makes sense that you pay for all of these services, because you’re using them after all. But the lack of predictability and sometimes arbitrary-feeling upcharges can be frustrating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Upstream
&lt;/h2&gt;

&lt;p&gt;This hourly pricing model has broken out of the raw infrastructure industry and into other providers who build on top of that infrastructure. Case in point, CodeSandbox.&lt;/p&gt;

&lt;p&gt;CodeSandbox is a “competitor” to &lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;RestlessIDE&lt;/a&gt;. I use the air quotes around “competitor” because they are 8 years old with 10s of millions in funding and a mature product, and I’m a guy in a WeWork. But also because the first thing I hear from people when I show them RestlessIDE is “hey, isn’t that a lot like what CodeSandbox does?” (For those interested, the answer is that there are superficial similarities, but much different approaches. I like ours better.)&lt;/p&gt;

&lt;p&gt;I first heard of CodeSandbox a few years back when I clicked on a URL to someone’s sandbox for some frontend Javascript tool. It was really neat, like an interactive console with a really good code editor. It felt just like a proper IDE. But I noticed that it didn’t have that one thing every IDE needs: A terminal. Curious.&lt;/p&gt;

&lt;p&gt;The next time I saw them was when I was doing competitive analysis for this RestlessIDE. I saw that they had a new type of offering, a VM sandbox, and that these had a terminal. Also that they were much more limited in terms of how long you could use them. 40 hours a month on the free plan and 100 for the $12 a month Editor plan, with additional hours being billed at $0.15 per. So a typical 160 hour work month is $21. That doesn’t sound too bad. But then there are the details.&lt;/p&gt;

&lt;p&gt;What is a workspace, and why do they have a certain number of members? Wait, does this mean that I need to split my 100 hours (up to) 20 ways? Does a workspace let me use multiple sandboxes with different libraries and code in each one? There are also VM tiers priced on the bottom of the page; where do those figure in? Are they in addition to the $12 a month VM credits? If I need to keep a particular sandbox alive 24 hours a day (because my client in Greece wants to check out my dev site, and I need it to be there whenever they choose to look) I’ll need to pay (checks math) $93?&lt;/p&gt;

&lt;p&gt;What I’m seeing here is a top line number that seems very reasonable, but a whole lot of uncertainty in terms of what that actually includes, and a deep suspicion that if I migrate my team to this tool for doing actual work I’ll get a big surprise at the end of the month. Or I might not. &lt;em&gt;I just can’t tell by looking at the pricing page.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I think what happened is that the unlimited Browser Sandboxes are the OG offering without the terminal, where all of the code can be served by a CDN and all the work is being done by your browser. This is perfect for working with frontend code, which is why those sandboxes were passed all around back in the day. The problem was that as cool as these were, it’s tough to do Real Work without access to a terminal and backend programming environment. CodeSandbox created a new type of sandbox (VM Sandboxes) which did have this capability, but then ran into the age-old problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I costs money to put containers on the web.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The team needed to figure out how to bundle these in a palatable way with a low price, while also making it financially sustainable for themselves. So they offered a generous free tier with the hope that once people tried it out they would upgrade to the paid version. A lot of the numbers shown on the pricing page like the number of members, etc, are sort of window dressing to increase the perception of value around what you get with higher tier plans. The real meat is the number of hours of VM Sandbox time.&lt;/p&gt;

&lt;p&gt;Please, I mean nothing here as a criticism of CodeSandbox, it’s a good tool. One friend that I surveyed is a happy customer on the paid plan and uses it for prototyping all sorts of frontend projects, and finds the bundled starter kits great for doing that. I’m just talking about cloud pricing in general, and am using them as a specific example of how it’s difficult. And they’re better at publishing prices than most. Most pricing pages have one column for free (for new users with almost no usage) and two columns with some variation of “Call Our Sales Team.”&lt;/p&gt;

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

&lt;p&gt;In the spirit of &lt;a href="https://restlessdev.com/2025/11/07/getting-the-model-right/" rel="noopener noreferrer"&gt;Getting the Model Right&lt;/a&gt;, I had to sit and think about what I was trying to do with RestlessIDE. What I settled on was this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Create an environment where development teams could move their processes to the web without compromises.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In order to achieve that, cloud pricing had to go. I thought about how we buy things in the real world. When you buy a computer, you expect to be able to use all of that computer for as long as you want. If you buy a hosting account for your website, you expect it to just stay online as long as you pay your monthly bill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is how humans process things.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s important to make your pricing model mesh with customer expectations, while also covering your own expenses. If you want to stay in business, of course.&lt;/p&gt;

&lt;p&gt;So RestlessIDE uses &lt;a href="https://restlesside.com/pricing/#hosts" rel="noopener noreferrer"&gt;Hosts&lt;/a&gt;, which are virtual machines you rent on a monthly basis. They have different specifications at different price points, and can include globs of memory if you need to keep a bunch of Workspaces online at the same time, large hard drives if you want to archive projects for years, or even GPUs if you need to work on AI or crypto projects and want some of the CUDA goodness.&lt;/p&gt;

&lt;p&gt;Once you have a Host, you can stuff it full of Workspaces or other services like databases, caches, or even remote desktops, and keep them online as long as you’d like. There is a bit more management overhead in that your Host has a fixed amount of resources, and you may need to turn off a service you aren’t using to make room for one you want to use, but that is very similar to what you need to do every day on your own machines.&lt;/p&gt;

&lt;p&gt;Is this the right model? I think so.&lt;/p&gt;

&lt;p&gt;Everybody uses services differently. What I loved about those earlier cloud IDEs was the fact I could create a new Workspace for every project and just have them all just be there when I needed them. As a consultant, I’ve worked on many dozens of projects. Some big, some small. Some where the client disappears for 3 years only to pop back with a spate of new requests.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I need to be able to be able to jump into a codebase quickly without needing to remember how to get it to work.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I don’t like to think about what version of NodeJS that particular project used, and then need to nvm over to it for that one task. Or where the development database happened to be when I worked on that project. (I go through a lot of computers!) Having it all sitting there in a container somewhere whenever I need it is wonderful.&lt;/p&gt;

&lt;p&gt;Under the cloud pricing model, there was always some sort of limitation I’m pushing against. Either you have to pay per workspace, or they shut themselves off after 30 minutes, or you can only actually keep them alive for a certain number of hours per month before hitting overages.&lt;/p&gt;

&lt;p&gt;But other people might have different priorities. If you’re just looking to jump in to some quick frontend code quickly, CodeSandbox is a great way to do that, and very affordable.&lt;/p&gt;

&lt;p&gt;I’m trying to make &lt;a href="https://restlesside.com/why-restlesside/" rel="noopener noreferrer"&gt;RestlessIDE&lt;/a&gt; a platform that can serve a daily driver for working developers and their teams. Where we anticipate needs and find solutions to them for you, and bake it all in so that you can just get on with your actual work. And yes, one where you know what you’ll be paying every month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Any arrangement you come up with will have upsides and downsides. I presented my plan to the Backend Standup crew and they were curious about certain details. “So you’re saying, if I want to have a 4th workspace, I need to rent a &lt;em&gt;whole server&lt;/em&gt;?” It’s a very reasonable question, but in context it makes sense. The per-workspace price would be about $10, whereas the smallest host is $15 and can also hold probably 10 more workspaces. 4 workspaces is a slightly awkward amount, but numbers 5-10 will be super cheap!&lt;/p&gt;

&lt;p&gt;Frames of reference are important. Coming up with a coherent pricing strategy involves a variety of factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Putting yourself in the shoes of your customers, and thinking through how they will use your application.&lt;/li&gt;
&lt;li&gt;  Figuring out where your own costs lie, and making sure they’re covered.&lt;/li&gt;
&lt;li&gt;  Understanding what your target market can bear in terms of price, and how they perceive value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can’t lean too hard on any one of those without falling over, and possibly getting replaced by someone who understands them better.&lt;/p&gt;

&lt;p&gt;Til next time.&lt;/p&gt;

&lt;p&gt;(Photo by &lt;a href="https://www.pexels.com/photo/copper-colored-coin-lot-259165/" rel="noopener noreferrer"&gt;Pixabay&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>development</category>
      <category>startup</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Receiving Webhooks in RestlessIDE</title>
      <dc:creator>Mike</dc:creator>
      <pubDate>Wed, 12 Nov 2025 21:15:59 +0000</pubDate>
      <link>https://forem.com/restlessmike/receiving-webhooks-in-restlesside-932</link>
      <guid>https://forem.com/restlessmike/receiving-webhooks-in-restlesside-932</guid>
      <description>&lt;p&gt;So I was speaking with the Backend Standup guys the other day (as one does) and one of them explained a problem they were having. They were attempting to do a  Stripe integration but were running into problems with the webhook portion.&lt;/p&gt;

&lt;p&gt;For those who need a reminder, a webhook is basically a callback from an online service into your application. For example, with Stripe (a payment processing service) you can set up subscriptions with your customers, where they sign up and pay you a certain amount each month. To handle this, Stripe generates an invoice automatically, and then charges them for it, again automatically. In order to give your application some visibility into this process, you can configure Stripe to hit a specified endpoint on your application with payment updates. This also applies to things like their card being declined or expired, or them canceling the subscription through Stripe directly. All very helpful things to be notified about.&lt;/p&gt;

&lt;p&gt;Anyway, my friend was having an issue because he was running his dev server on his local machine, which doesn’t have an external hostname, and thus didn’t have a URL he could tell Stripe to hit. What to do?&lt;/p&gt;

&lt;p&gt;If he were using RestlessIDE (the future &lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;best web-based development environment&lt;/a&gt;!) this would be a non-issue. Here are the steps to set it up:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create your workspace with either Port 3000 or Port 3030 set to HTTP/HTTPS.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdp0lybp7bqpjd6kixit7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdp0lybp7bqpjd6kixit7.png" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s probably also a good idea to use SSL. Setting this up will tell your container to open up the port for external access over HTTP, which will be needed for the webhook.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Set up your application to listen on the port you chose.
&lt;/h3&gt;

&lt;p&gt;The details will vary a bit depending on the specifics of your programming language/environment. In NodeJS, we typically just add a &lt;code&gt;PORT=3000&lt;/code&gt; to our &lt;code&gt;.env&lt;/code&gt; and the corresponding code in the application to look there. If you’re using PHP, RestlessIDE automatically configures your web server to listen on Port 3000, so you should be all set.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Configure the service you’re working with to hit your RestlessIDE application.
&lt;/h3&gt;

&lt;p&gt;Your Workspace will be working on a particular host name, in the format:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://&amp;lt;workspace&amp;gt;-&amp;lt;username&amp;gt;.hosts.restlesside.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is where you edit your code. To access your running application (once you start it, that is!) you can just go to that same URL with Port 3000 like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://&amp;lt;workspace&amp;gt;-&amp;lt;username&amp;gt;.hosts.restlesside.com:3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You’ll need to set up your application to receive the webhook, which often involves a handshake step as you’ll need to verify that incoming request is indeed coming from the service you’re expecting. You’ll need to create an endpoint for this endpoint, let’s say &lt;code&gt;/webhooks/receive&lt;/code&gt;. Once it’s all configured you’ll need to register this webhook somewhere in service’s UI. Just enter:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://&amp;lt;workspace&amp;gt;-&amp;lt;username&amp;gt;.hosts.restlesside.com:3000/webhooks/receive&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;in the appropriate spot and you should be good to go.&lt;/p&gt;

&lt;p&gt;See, things get a lot easier if you’re coding the web &lt;em&gt;from the web&lt;/em&gt;. If you’re not convinced, here are &lt;a href="https://restlesside.com/#reasons" rel="noopener noreferrer"&gt;10 more reasons why coding in a browser is the best&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Til next time.&lt;/p&gt;

&lt;p&gt;(Photo by &lt;a href="https://unsplash.com/@steve_j?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Steve Johnson&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/gray-scale-photo-of-door-lever-6sB8gMRlEAU?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash)&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Getting the Model Right</title>
      <dc:creator>Mike</dc:creator>
      <pubDate>Mon, 10 Nov 2025 22:11:16 +0000</pubDate>
      <link>https://forem.com/restlessmike/getting-the-model-right-35dd</link>
      <guid>https://forem.com/restlessmike/getting-the-model-right-35dd</guid>
      <description>&lt;p&gt;As a developer, I’ve been a round the block a few times. Danced with the devil and lived to tell the tale. I’ve &lt;em&gt;seen&lt;/em&gt; things.&lt;/p&gt;

&lt;p&gt;Among my favorite parts of the job is that indescribable joy when you’ve built something where every new feature request works perfectly with what you’ve already got in place. Where it’s just adding a field there, or dropping in a new component that just flows with everything you’ve already done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I call this “getting the model right.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s validation that the way you approached the problem was meaningfully correct, at least for now.&lt;/p&gt;

&lt;p&gt;There are a few different components to accomplishing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The decisions you make within your code.&lt;/li&gt;
&lt;li&gt;  The technical details of the technologies you use to build your project.&lt;/li&gt;
&lt;li&gt;  The &lt;em&gt;culture&lt;/em&gt; you bring to your code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this essay I’m going to explore each these in more detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Worlds
&lt;/h2&gt;

&lt;p&gt;Being a programmer is a lot different from most other fields in that we get to (in many ways) create the world that we inhabit. Sure, there are rules (both those defined by the languages and platforms we use, and the organizations that pay us) but from day to day, we live within our own creation.&lt;/p&gt;

&lt;p&gt;I’ll set the scene:&lt;/p&gt;

&lt;p&gt;You’re a programmer working for United Widget, Inc. The company wants to build a new portal for their customers to log in and place new orders for widgets and see the status of existing orders. It’s a brand new project with no legacy cruft.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;That’s a bingo!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your first task is to decide which platform you want to use to build this portal. You have a lot of experience with PHP, but don’t like the language much and want to learn something new. So you decide to use a NodeJS backend with a frontend built in React. For a database you decide to use PostgreSQL since you know it so well already.&lt;/p&gt;

&lt;p&gt;This is your first choice, the dirt, wood and stones from which you will build your palace. These early selections will give you the conventions you’ll be using from now on, so they’re very important. We’ll dig more into &lt;em&gt;why&lt;/em&gt; later.&lt;/p&gt;

&lt;p&gt;Let’s get to building. Being prudent, you decide to first put together a quick schema for the application.&lt;/p&gt;

&lt;p&gt;People will need to login, obviously, so you’ll make a &lt;code&gt;users&lt;/code&gt; table. They’ll have email addresses, (which they’ll use to login) first names, last names, and passwords, which you’ll probably want to hash for security reasons. And a unique identifier field so we can not have a piece of malleable user data in multiple tables. Oh, and they’ll need to belong to some sort of customer group, so that multiple people from the same company can log in. Let’s make a &lt;code&gt;companies&lt;/code&gt; table. No, on second thought, let’s call it &lt;code&gt;organizations&lt;/code&gt;, so that it is more generic and doesn’t imply a commercial company. Smart.&lt;/p&gt;

&lt;p&gt;We’re only on the first step of this process, and you can already see this world taking shape. These concepts of &lt;em&gt;users&lt;/em&gt; and &lt;em&gt;organizations&lt;/em&gt; will become real, and will show up in reports for senior management for years to come. Let’s continue.&lt;/p&gt;

&lt;p&gt;The organization also needs some fields. Let’s give it a unique ID, name, a bunch of address fields, and a flag to indicate if they’re active or not. Hmm. Maybe we should make &lt;code&gt;addresses&lt;/code&gt; a separate table and associate it with the organization by &lt;code&gt;address_id&lt;/code&gt;, so we can know when different organizations share the same address. But what if one of them changes it for both, or if one enters it all with caps lock on, and the other hates that? On second thought, nevermind, we’ll just keep those fields on &lt;code&gt;organizations&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ok, now we have to figure out how we assign &lt;code&gt;users&lt;/code&gt; to &lt;code&gt;organizations&lt;/code&gt;. Maybe we should add an &lt;code&gt;organization_id&lt;/code&gt; to the &lt;code&gt;users&lt;/code&gt; table, so that each organization can own their users. It could make sense since we’re dealing with mostly company emails.&lt;/p&gt;

&lt;p&gt;Ah, &lt;em&gt;&lt;strong&gt;mostly&lt;/strong&gt;.&lt;/em&gt; This is the first fork in the road, and we’re only two tables in.&lt;/p&gt;

&lt;p&gt;It turns out that some customers have external contracting agencies order their widgets for them, and that some people from these agencies work for multiple customers. Since we decided to make email address part of the user’s login credentials, we need to have the ability for users to choose their organization they are acting on behalf of &lt;em&gt;after&lt;/em&gt; logging in, meaning we can’t have users belong to one and only one organization. From a database perspective, that typically means we’ll need a third table (perhaps &lt;code&gt;organization_users&lt;/code&gt;) that associates users with multiple organizations by ID, and possibly contains additional data about the user, like their role within the specific organization. From an application standpoint, it means that we need to put limits on an organization’s ability to edit a user’s information, like their name and perhaps even their email address, because that user’s data has to appear for multiple customers and you don’t want them to be stomping over each other’s data and reports.&lt;/p&gt;

&lt;p&gt;If we had chosen to put an &lt;code&gt;organization_id&lt;/code&gt; on the &lt;code&gt;users&lt;/code&gt; table, &lt;strong&gt;we would have gotten the model wrong.&lt;/strong&gt; For this very specific example, anyway.&lt;/p&gt;

&lt;p&gt;And this is only the very first step of the process. In our application, we need to choose everything from server-side routing frameworks to UI widget libraries, each one of which has their own assumptions, strengths, and weaknesses, and repercussions down the line. This is all before we have written a &lt;em&gt;single line of code&lt;/em&gt;. Once that starts, a whole new set of decisions must be made:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  What do we want our backend’s API to look like? Is it a simple REST API, or do we want to do something like GraphQL? If it’s REST, what should the URL structure look like?&lt;/li&gt;
&lt;li&gt;  How do we organize the code? Do we use raw database libraries or try to use an ORM? How much SQL in the code is too much SQL?&lt;/li&gt;
&lt;li&gt;  On the frontend, how do we organize our components? How do we handle routing, and what does that structure look like?&lt;/li&gt;
&lt;li&gt;  And so much more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each one of these decisions will ripple through to future developers, and perhaps even their children if the project lasts long enough and they happen to get a job at the same company doing the same thing as their parents. Hey, it could happen.&lt;/p&gt;

&lt;p&gt;These decisions are all part of the model of your application, the part over which you have the most direct control. Software is a living thing, especially when it’s meant to last. The soundness of its design and the choices that went into it speak to how well it can continue to serve its purpose.&lt;/p&gt;

&lt;p&gt;So how can we as programmers be safe in this dangerous pursuit?&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Abstractions
&lt;/h2&gt;

&lt;p&gt;Programmers work with what are called &lt;strong&gt;abstractions&lt;/strong&gt;: we create representations of real-world phenomena that hide a lot of the actual complexity of that phenomena, allowing us to narrow in on the details that are important to us. In many ways this is more an art form than a science, but it gets easier with practice as you come to develop a better sense of what is important and what you can let go.&lt;/p&gt;

&lt;p&gt;One very popular paradigm that many programming languages implement is called &lt;em&gt;object-oriented programming&lt;/em&gt;. It was especially popular starting in the mid-1990s with Java and C++, but it continues to have adherents today in those languages and others like Python and C#.  For the uninitiated, object-oriented languages make this idea of “abstractions” very explicit. You could have code like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class User {
  public string emailAddress;
  public string firstName;
  public string lastName;

  public static User login(string emailAddress, string password) {
    // ...
  }
}

public class Manager extends User {
  public User[] employees;

  public void addEmployee(User employee) {
    // ...
  }

  public void removeEmployee(User employee) {
    // ...
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can clearly see the real-world ideas of a user and a manager, and that the manager is a specialized type of user which has the ability to add and remove employee users from their employee list. But you can also see all of the stuff that we left out. Maybe those users have children themselves. They definitely have a hairColor, and a shoeSize, but we don’t really care about that for our purposes, so we kept it out of the model.&lt;/p&gt;

&lt;p&gt;For these reasons (and others), these languages were often used for modeling business processes, as any programmer familiar with the conventions of the language can (probably) make sense of what we’re trying to represent. This &lt;em&gt;discoverability&lt;/em&gt; is very important, as it allows people who didn’t originally write the code quickly get up to speed.&lt;/p&gt;

&lt;p&gt;In addition to object-oriented programming, there are other paradigms that are commonly used. Most recently, &lt;em&gt;functional programming&lt;/em&gt; has been making lots of waves, with its use in languages like Erlang, Haskell, and (especially) Javascript/Typescript. (Side note: Most languages today aren’t strictly one paradigm or another, and often incorporate elements of other paradigms when it makes sense. The degree to which they do this can change over time, and the &lt;em&gt;culture&lt;/em&gt; around the language can influence the relative popularity of different paradigms within a language over time.)&lt;/p&gt;

&lt;p&gt;Functional programming lends itself very well to modeling event-driven processes, which happens to be very applicable to things like graphical user interfaces and protocols like HTTP that rely on requests and responses over the network.&lt;/p&gt;

&lt;p&gt;In my opinion, functional programming requires a bit more work on the part of the programmer than object-oriented programming, in that they need to keep more context in their head as they do it. With (well-designed) object-oriented classes, everything is simple and laid out in front of you. This class has these fields, and this method takes these arguments. If you want to call it you need to figure out where each one of those arguments is coming from.&lt;/p&gt;

&lt;p&gt;On the other hand, functional languages don’t always have a direct path through the code. In functional Javascript, you can easily find yourself in “callback hell”, where your functions call other functions, which may or may not return properly, and it’s difficult to know if your overall operation succeeded until you know the outcome of each of the nested functions. There’s even a &lt;a href="https://callbackhell.com/" rel="noopener noreferrer"&gt;website&lt;/a&gt; for it. Yes, you can mitigate it through proper error handling, or through using promises, or a bunch of other strategies. The point is that you need to think through more edge cases, and this takes a mental load.&lt;/p&gt;

&lt;p&gt;This is the price to be paid for the efficiency the language offers for the task at hand, and it’s a &lt;em&gt;totally acceptable price&lt;/em&gt;, but one that must be paid none the less.&lt;/p&gt;

&lt;p&gt;If not managed correctly, this additional mental load can impact a program’s discoverability. The programmer that originally writes the module knows exactly what’s going on, and has all of the context for the operation in their head at the moment they’re writing the code. Five years later, the next guy to look at that code might have none of that. If it isn’t &lt;em&gt;encapsulated&lt;/em&gt; well, the new guy will have to do a lot of learning and experimenting to understand what’s going on.&lt;/p&gt;

&lt;p&gt;Any decently-sized application will need to choose the right paradigm for the right task, &lt;em&gt;and will need to execute it well&lt;/em&gt; if the programmer hopes to get the model right.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is this “culture” you speak of?
&lt;/h2&gt;

&lt;p&gt;I think Steve Jobs summarizes it well in this clip from 1995:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/3KdlJlHAAbQ"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Culture in this sense is the deep understanding of &lt;strong&gt;why&lt;/strong&gt; something is good and necessary, &lt;strong&gt;how&lt;/strong&gt; best to incorporate it into your product, and &lt;strong&gt;what&lt;/strong&gt; the benefits will be once it is added. It’s the product of lots of experience and observation.&lt;/p&gt;

&lt;p&gt;Some aspects of culture come from the technologies you use.&lt;/p&gt;

&lt;p&gt;Programming has a vast number of subcultures, and this &lt;em&gt;heavily&lt;/em&gt; influences the experience of anyone using a certain language. For example, the language PHP is historically used for quick-and-dirty web applications. The culture around it tends to prioritize getting things done quickly and doesn’t give a damn about aesthetics. The primary documentation site, php.net, has a function reference section where all of the build-in functions are shown; each of these pages has a comment section where for the last 30 years people have been posting solutions to some esoteric problem they ran into and the solution they came up with, and PHP developers think nothing of scouring this archive and copy-pasting that code directly into their new application. (Side note: It is possible to write well-constructed PHP code. I think I saw some once.)&lt;/p&gt;

&lt;p&gt;Java programmers, on the other hand, tend to be on the &lt;em&gt;enterprise&lt;/em&gt; side. This language is often used at larger enterprises that adopted it back in the 1999 when some salesperson convinced the CTO to buy IBM Websphere. These programmers have lots of meetings where they bring in entity diagrams and flowcharts and discuss multiyear timelines for when the software will be released. I am poking fun a little, but I don’t mean this in a pejorative way; the people who write Java applications tend to put the time into understanding what the application needs to do and work out the contingencies ahead of time, which makes sense when the software is powering a billion-dollar company and any downtime costs multiple peoples’ annual salaries.&lt;/p&gt;

&lt;p&gt;More recently, we’ve seen a subculture coalesce around Meta’s ReactJS, a Javascript/Typescript library traditionally used for single-page applications. It seems like every week there is a new framework or component library released for React, trying to make the library easier to use, or filling in some new niche. From the outside, it seems like this subculture embraces rapid iteration, code elegance and aesthetics. Typically new entrants announce themselves with beautiful websites and concise tutorials.&lt;/p&gt;

&lt;p&gt;The shared context people get from using a technology intensively helps reinforce “conventional wisdom” with that group, and can provide guardrails if applied carefully. But you can’t stop there.&lt;/p&gt;

&lt;p&gt;Truly understanding culture involves critically looking at that conventional wisdom, and figuring out what parts work for you and which don’t.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no right and wrong in programming.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To be clear, there is shoddy work and quality work, but for any process you follow or ritual you do &lt;em&gt;every single time&lt;/em&gt;, there is an argument to be made for &lt;strong&gt;not&lt;/strong&gt; doing it that way in a particular circumstance. Except maybe using version control. Commit your code, everyone.&lt;/p&gt;

&lt;p&gt;More specifically, the culture you bring to &lt;em&gt;your code&lt;/em&gt; and &lt;em&gt;your project&lt;/em&gt; informs how well it will get the model right. Your experience tells you when to be super uncompromising about a certain segment of the code, and which parts you can be quick-and-dirty with to save time, and your application of those standards becomes the culture you pass down to your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  That was a lot of words.
&lt;/h2&gt;

&lt;p&gt;Yes, it was. I’m working on some other essays and I wanted to get all of this out there first, as I’ll be referring back to it.&lt;/p&gt;

&lt;p&gt;Think of this as my tapping sign.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/7ZfqdrI5HgI"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Thank you for making it all the way to the end! While you’re still here, I’d be remiss if I didn’t mention RestlessIDE, the (future) &lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;best web-based development environment&lt;/a&gt; in the world. You should try it out, I think you’d like it.&lt;/p&gt;

&lt;p&gt;Til next time.&lt;/p&gt;

&lt;p&gt;(Photo by &lt;a href="https://unsplash.com/@jaimespaniol?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Jaime Spaniol&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/brown-sand-castle-l4JygnDCbMg?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>architecture</category>
      <category>discuss</category>
    </item>
    <item>
      <title>RestlessIDE Enters Public Beta</title>
      <dc:creator>Mike</dc:creator>
      <pubDate>Wed, 05 Nov 2025 14:59:27 +0000</pubDate>
      <link>https://forem.com/restlessmike/restlesside-enters-public-beta-21ke</link>
      <guid>https://forem.com/restlessmike/restlesside-enters-public-beta-21ke</guid>
      <description>&lt;p&gt;We’re happy to announce that the (future) &lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;best web-based development environment&lt;/a&gt; is finally entering public beta! This is a huge step for us here at RestlessDev as it’s our first Software as a Service product, and the culmination of two plus years of work. To celebrate the occasion we’ve launched a new website to help explain our vision and what sets us apart. Please &lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;check it out&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;At this point, all major features of the application should be working, although there may be some specific parts that may not have been hit on as much during the private beta. With that in mind, we’re offering enhanced support for anyone joining during the public beta period.  Once you sign up we’ll reach out personally to help walk you through the signup process and make sure RestlessIDE meets your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  So What’s This RestlessIDE Then?
&lt;/h2&gt;

&lt;p&gt;It’s been a little bit since our last flurry of posts, so here is a quick refresher for those who forget or who may have missed the previous announcements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RestlessIDE is a web application that seeks to empower you and your team to do all of your development work from a browser.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core of the service is the &lt;a href="https://restlesside.com/features/#workspaces" rel="noopener noreferrer"&gt;Workspace&lt;/a&gt;: An individual container that lets you work on your code from within a VS Code-compatible environment in your browser. You have full terminal access to install the libraries and other tools you need, and your workspace also has (up to) two available ports for external access so you can see the output of your application in real time. You can create additional Workspaces as needed for each one of your projects, keeping them all isolated from one another and letting you set up the ideal environment for each.&lt;/p&gt;

&lt;p&gt;We didn’t stop there, though. As you dig in to RestlessIDE, you can add additional features to your account through adding Hosts, which are virtual machines that you can pack full of other development and collaboration tools. These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://restlesside.com/features/#databases" rel="noopener noreferrer"&gt;Development databases&lt;/a&gt; such as PostgreSQL, MySQL and MongoDB, each with web-based admin tools to let you define, see and modify your data right in the browser, and access it from any Workspace or legacy desktop IDEs.&lt;/li&gt;
&lt;li&gt;  Caches including Redis and Memcache, also with web-based admin interfaces.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://restlesside.com/features/#remote-desktops" rel="noopener noreferrer"&gt;Remote Desktops&lt;/a&gt; with an assortment of window managers, to let you access tools like Podman (a Docker-compatible container runtime), Gimp (a Photoshop-like image editor) and Filezilla (a SFTP/FTP application for when you need it) as well as any other desktop applications that don’t have good web equivalents. All available in the browser.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://restlesside.com/features/#collaboration" rel="noopener noreferrer"&gt;Zulip&lt;/a&gt;, an open-source team messaging app with features similar to Slack, with both web and mobile interfaces.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://restlesside.com/features/#collaboration" rel="noopener noreferrer"&gt;OpenProject&lt;/a&gt;, an open-source project management suite.&lt;/li&gt;
&lt;li&gt;  Additional Workspaces, which can be set up with various CPU and memory configurations, and (on supported Hosts) can even have access to GPUs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is just our initial lineup of features. Over time we will be refining the available services we support, bringing in other open-source tools depending on what our users need and ask for.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sets RestlessIDE Apart?
&lt;/h2&gt;

&lt;p&gt;Programming in the browser isn’t exactly new.&lt;/p&gt;

&lt;p&gt;What sets us apart from others in the field comes down to a few main things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We are trying to focus on the parts of the developer experience that fall outside of just editing code. With features like remote desktops and database admin tools, we’re trying to provide the “last mile” to let you break free from legacy desktop development. You should be able to jump online from any machine and just work.&lt;/li&gt;
&lt;li&gt; Our pricing model is different from other cloud providers. Where others have you pay piecemeal for each service, often on an hourly or other awkwardly-metered basis, we offer a wide variety of &lt;a href="https://restlesside.com/pricing/#hosts" rel="noopener noreferrer"&gt;Hosts&lt;/a&gt; that can be rented on a monthly basis to provide capacity you can use however you want. Once you have a Host, you can install various services, use them for a while, turn them off when you don’t need them to free up resources, install new ones as needed, and a whole lot more. No more zombie services that you pay for just because you don’t know if you’ll need them again, and no more surprises at the end of the month when you suddenly crossed some threshold and triggered a new pricing tier.&lt;/li&gt;
&lt;li&gt; We’re not chasing trends. For example, our Workspaces do support AI extensions (via the &lt;a href="https://open-vsx.org/?search=ai&amp;amp;sortBy=relevance&amp;amp;sortOrder=desc" rel="noopener noreferrer"&gt;OpenVSX&lt;/a&gt; registry) we aren’t making a big deal about it. You’ll notice AI was only mentioned as the last item on the last list of this page. Like it should be. We are focused on trying to get the simple things right, and to grow from there, finding new ways to bring value to our platform.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can read more about our &lt;a href="https://restlesside.com/why-restlesside/" rel="noopener noreferrer"&gt;unique value proposition&lt;/a&gt; on the site.&lt;/p&gt;

&lt;p&gt;We’re very excited to finally get RestlessIDE out there, and can’t wait for you to try it out. Why not &lt;a href="https://app.restlesside.com/signup" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; today?&lt;/p&gt;

&lt;p&gt;Til next time.&lt;/p&gt;

&lt;p&gt;(Photo by &lt;a href="https://unsplash.com/@spensersembrat?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Spenser Sembrat&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/man-in-orange-coat-overlooking-arid-mountainous-landscape-EPuHuWQEXJU?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>10 (or so) Things You’ll Love about RestlessIDE</title>
      <dc:creator>Mike</dc:creator>
      <pubDate>Mon, 14 Jul 2025 14:56:59 +0000</pubDate>
      <link>https://forem.com/restlessmike/10-or-so-things-youll-love-about-restlesside-40bg</link>
      <guid>https://forem.com/restlessmike/10-or-so-things-youll-love-about-restlesside-40bg</guid>
      <description>&lt;p&gt;(Photo by &lt;a href="https://www.pexels.com/photo/man-holding-baby-s-breath-flower-in-front-of-woman-standing-near-marble-wall-935789/" rel="noopener noreferrer"&gt;Vija Rindo Pratama&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Here at RestlessIDE HQ, we are never shy about tooting our own horn. Somebody’s got to do it, right? You can’t become the &lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;best web-based development environment&lt;/a&gt; by sitting in the corner politely.&lt;/p&gt;

&lt;p&gt;But it’s one thing to tell, and another to show. Here are 10 (or so) things we think you’ll love about RestlessIDE.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. It’s VS Code, in the browser.
&lt;/h2&gt;

&lt;p&gt;We use the brilliant &lt;a href="https://github.com/gitpod-io/openvscode-server" rel="noopener noreferrer"&gt;OpenVSCode Server&lt;/a&gt; from Gitpod.io to power our editor workspaces. This project pulls directly from Microsoft’s VS Code repository and makes it work within a container, accessed via a web browser. Everything will immediately feel familiar to you because it’s what you’ve been coding in for the last decade.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fil6o6iprinnxf6s7h0n6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fil6o6iprinnxf6s7h0n6.png" width="800" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. We don’t limit your VM usage.
&lt;/h2&gt;

&lt;p&gt;We know the grind and we respect it. We don’t put limits around the usage of your workspaces or charge you extra for working more. If you want to pull 60 hour weeks, RestlessiDE will be right there with you. If we had arms, we’d bring you coffee and maybe a little shoulder rub. We’re just a website though.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Built-in dev sites.
&lt;/h2&gt;

&lt;p&gt;Every workspace can have up to two different ports open to the outside world. If you’re working on a website, you can publish to port 3000 or 3030 inside your workspace and then access your dev site from that port to see your work in real time. We provision SSL certificates for web sites automatically, so you can publish straight HTTP and you’ll still be HTTPS. You can also open raw TCP ports if you’re working on something that isn’t meant for the web.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. KeepAlive Workspaces.
&lt;/h2&gt;

&lt;p&gt;KeepAlive workspaces let you keep your workspaces alive (um) &lt;em&gt;even when you aren’t currently using them.&lt;/em&gt; Think about it. You’re hacking away on some website update for your client, and you want to show them, but they’re 3 timezones away. You send them your dev URL but they might not look at it in the next 30 minutes. What to do? KeepAlive workspaces to the rescue. Flip the switch on your Workspace page from within RestlessIDE and we won’t shut the workspace down after 30 minutes.&lt;/p&gt;

&lt;p&gt;Each account can support one KeepAlive workspace at a time on our shared hosts. If you get your own host, you can designate any workspace on there as KeepAlive. We actually use 4 KeepAlive workspaces for our own dev site, they’re awesome.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc7ktbeaum7c6ho13vzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc7ktbeaum7c6ho13vzi.png" width="800" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. We have AI!
&lt;/h2&gt;

&lt;p&gt;It’s hard to be a programmer today without taking advantage of artificial intelligence. Although we’re stodgy graybeards at heart (get off our lawn!) we have been known to use some AI assistance on occasion.&lt;/p&gt;

&lt;p&gt;RestlessIDE uses VS Code at heart (thanks to Gitpod.io’s OpenVSCode Server) and can subsequently use a variety of AI plugins built for that platform. While we haven’t tried them all, most that connect to external APIs should work out of the box. Pictured below is &lt;a href="https://marketplace.visualstudio.com/items?itemName=kilocode.Kilo-Code" rel="noopener noreferrer"&gt;Kilo Code&lt;/a&gt;, a promising new open source plugin that lets you access your favorite models (including Gemini 2.5 Pro, Claude 4 Sonnet &amp;amp; Opus, and GPT-4.1) through various modes to build your apps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl45ne58cfirafb3pim1r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl45ne58cfirafb3pim1r.png" width="544" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Configure once, run whenever.
&lt;/h2&gt;

&lt;p&gt;How many times have you gone back to a particular project only to find it was in Node 8 or relied on some old library that choked on anything over Python 2.7? With RestlessIDE, you can set up your workspace specifically for each project, and then, once you’re done with it, just turn it off. It just sits there, in the background, waiting for the day when you need it again. When that client calls you can just start it up again and be back in business.&lt;/p&gt;

&lt;p&gt;Incidentally, this also cuts down a lot on the configuration needed when you get a new computer. Just point your browser to &lt;a href="https://app.restlesside.com" rel="noopener noreferrer"&gt;https://app.restlesside.com&lt;/a&gt; and you’re productive right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Build out your infrastructure with Hosts.
&lt;/h2&gt;

&lt;p&gt;When you’re ready to graduate to RestlessIDE Power User status, you can get a Host or two (or more). Hosts are virtual machines that you can stuff full of additional workspaces and services (more on those below!) for your entire team. Each of these VMs can have CPU, memory, disk space and GPU specs tailored to your organization’s unique needs.&lt;/p&gt;

&lt;p&gt;Adding a Host to your account opens up the world of Services, which we’re going into more below. They start at $15 for 1 Core/2GB RAM/50GB Disk and go up linearly from there!&lt;/p&gt;

&lt;h2&gt;
  
  
  8. We work in heterogeneous environments.
&lt;/h2&gt;

&lt;p&gt;Just because &lt;em&gt;you&lt;/em&gt; see the light and realize that all development should be done inside a web browser doesn’t mean that Linus over on the backend team feels the same way. He has a Threadripper under his desk and gosh darn it, he wants to use it.&lt;/p&gt;

&lt;p&gt;We support that! Different strokes for different folks.&lt;/p&gt;

&lt;p&gt;All of the databases and other resources you set up on your Hosts are accessible outside of RestlessIDE, so you and Linus can still work in a common environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Our Hosts can have GPUs.
&lt;/h2&gt;

&lt;p&gt;Some modern workloads such as Artificial Intelligence and Crypto require workspaces with GPUs. If you fall into that camp, we have your back!&lt;/p&gt;

&lt;p&gt;We currently support 3 different nVidia A16 configurations, and can access others if it turns out customers want them. Once you get a GPU Host you can add workspaces to it just like any other, and they will get access to the GPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Our Dev Databases come with GUIs.
&lt;/h2&gt;

&lt;p&gt;One of the more useful services we offer are free dev instances for PostgreSQL, MySQL, MongoDB, Redis, and Memcache that you can install on your Hosts. These are tools people often need to set up for their team and we make it a few quick clicks.&lt;/p&gt;

&lt;p&gt;More than that, we also provide them with web-based GUIs! No more scrambling for a local solution and trying to figure out how to authenticate to even get started. You can jump in right away, set up your instance, preload your data, or whatever else you need. You can also see if your caches are being hit via their web UIs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2sy73wx0dpzq2eiwb81s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2sy73wx0dpzq2eiwb81s.png" width="489" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Remote Desktops
&lt;/h2&gt;

&lt;p&gt;As great as the web is for many things, there are certain tasks that just require an actual desktop. For example, over the years I’ve done a lot of work with WordPress sites that require SFTP to push files and debug things. I have yet to see a good web version of an SFTP client. We also use desktops for building Podman images and pushing them where then need to go.&lt;/p&gt;

&lt;p&gt;We have four different desktop environments to choose from, and your desktop is always private just to you. (For security reasons, we require each desktop be assigned to an individual user on your account to ensure privacy when using Password Managers and the like.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxi6j542kz5emk5a7669.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxi6j542kz5emk5a7669.png" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Zulip Chat
&lt;/h2&gt;

&lt;p&gt;Team chat has changed the way we communicate at work. The modern workplace can feature collaborators from time zones (or even oceans) away, and having a solid chat solution to share ideas, solutions, and even memes and jokes is critical. It’s the modern water cooler and meeting room, all in one.&lt;/p&gt;

&lt;p&gt;RestlessIDE allows for a quick install of Zulip team chat on your Host. &lt;a href="https://zulip.com/" rel="noopener noreferrer"&gt;Zulip&lt;/a&gt; is an open-source application that contains all of the features you know and love from Slack or Teams (or maybe not Teams?) but in a self-hosted environment. You can access it remotely using their mobile apps while keeping your data on your Host. It even has video meetings with Zoom integration if you are a customer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flg2xny6utlpqhmtwbkrk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flg2xny6utlpqhmtwbkrk.png" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  13. OpenProject
&lt;/h2&gt;

&lt;p&gt;One of the hardest parts of working on a software team is keeping everyone organized. We programmers can be an unruly lot; the power that flows through our fingers often goes straight to our heads. It’s science.&lt;/p&gt;

&lt;p&gt;Anyway, RestlessIDE features &lt;a href="https://www.openproject.org/" rel="noopener noreferrer"&gt;OpenProject&lt;/a&gt;, a (you guessed it) open-source software package that lets you create and assign tickets, set timelines, build Gantt charts(!), write wiki pages, and manage your projects through whichever strategy works for you. Your whole team on RestlessIDE can log in using the same credentials, and you can configure it to send everyone emails at an alarming rate just like Jira, if you want to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkadaydqmorvbmaqpf735.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkadaydqmorvbmaqpf735.png" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As you can see, RestlessIDE has a little something for everyone. If you’d like to experience it for yourself (and give us feedback, because we’re still growing and refining it!) please &lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;sign up for our beta!&lt;/a&gt; We can’t wait to show you what we can do.&lt;/p&gt;

&lt;p&gt;Til next time.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>RestlessIDE Enters Closed Beta</title>
      <dc:creator>Mike</dc:creator>
      <pubDate>Wed, 25 Jun 2025 15:01:16 +0000</pubDate>
      <link>https://forem.com/restlessmike/restlesside-enters-closed-beta-41p1</link>
      <guid>https://forem.com/restlessmike/restlesside-enters-closed-beta-41p1</guid>
      <description>&lt;p&gt;(Photo by &lt;a href="https://www.pexels.com/photo/chihuahua-celebrating-with-colorful-balloons-32552330/" rel="noopener noreferrer"&gt;morgana pozzi&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;So I’ve been working on something this past little while, and it’s getting to the point where it’s ready to see the light of day. &lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;RestlessIDE&lt;/a&gt; is entering its closed beta! If anyone is interested in checking it out during this early phase, offering feedback, suggestions, and maybe a few bug reports, I’d really appreciate it. Please just sign up on the site and I will follow up with you as beta slots become available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;Join the Beta&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is RestlessIDE?
&lt;/h2&gt;

&lt;p&gt;RestlessIDE is my take on a web-based Integrated Development Environment. For those that aren’t developers, an IDE is typically where a programmer does their coding. Most often this is done on their own (or their company/organization’s own) computers, but with RestlessIDE they can do it right in their web browser from any computer with internet access.&lt;/p&gt;

&lt;p&gt;There are other web-base development environments out there; what makes RestlessIDE compelling?&lt;/p&gt;

&lt;h3&gt;
  
  
  1. A Great Code Editor
&lt;/h3&gt;

&lt;p&gt;RestlessIDE uses &lt;a href="https://github.com/gitpod-io/openvscode-server" rel="noopener noreferrer"&gt;OpenVSCode-Server&lt;/a&gt; as the core of its editing interface. This project takes the open-source VS Code editor we all know and love and makes it available inside a Docker container. You get the same day-to-day coding experience you’ve become accustomed to, with (most of) the same plugins and other add-ons, but inside a web browser. Pretty neat, huh?&lt;/p&gt;

&lt;p&gt;Your code is available wherever you are. Throw your old Chromebook in your bag and log on from the train. Jump on your boyfriend’s parents’ computer during Thankgiving (sometime after the 2pm dinner and before the Lions game) and bang out some code. Do you have a NexDock and a Samsung Galaxy phone? Plug it in and get-a-codin’!&lt;/p&gt;

&lt;p&gt;Each RestlessIDE workspace lets you customize your container to the needs of a specific project. Are you a freelancer or consultant who needs to jump between different codebases, each with separate infrastructure, libraries, etc? Just create separate workspaces, configure them as needed, and they’ll be there whenever you need them.&lt;/p&gt;

&lt;p&gt;When configuring a new workspace, you can specify a git repository (even private ones!) and it will automatically get cloned into your workspace. You can also specify a language/platform and the workspace will install compilers and libraries for you to get you started faster. You can even have it install a local database right within your workspace, with MySQL, PostgreSQL and MongoDB supported at launch.&lt;/p&gt;

&lt;p&gt;Because your workspace is already running in the cloud, you (and your clients) can easily see your work in progress just by going to a special port on your workspace’s domain. Each workspace can have up to two ports available (3000 and 3030) and they can be configured for HTTP/HTTPS (with automatic SSL certificates) or raw TCP connections, depending on your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Your Dev Infrastructure, on the Web
&lt;/h3&gt;

&lt;p&gt;Modern programming is a lot more than just editing code, though. You need to rely on lots of other tools and other infrastructure to get things done.&lt;/p&gt;

&lt;p&gt;RestlessIDE offers a number of add-on Services that make it easier to work entirely on the web.&lt;/p&gt;

&lt;p&gt;These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  PostgreSQL, MySQL and MongoDB, each bundled with their respective web-based administration tools. You can use these to set up development databases for the whole team, keeping data centralized and easy to access, while avoiding unnecessary costs from other cloud providers.&lt;/li&gt;
&lt;li&gt;  Redis and Memcache, also bundled with web-based administration interfaces.&lt;/li&gt;
&lt;li&gt;  Web-based Linux Virtual Desktops: Give your developers a full desktop for running any of the tools they need (including Podman, a tool similar to Docker that runs in non-root accounts, and Filezilla, a graphical SFTP client) that don’t work easily from within their workspaces.&lt;/li&gt;
&lt;li&gt;  Collaboration tools, including Zulip and OpenProject.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list of Services will grow over time based on community feedback, and are generally free, with the caveat that they need to be run on your organization’s private Hosts, described below.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Room to Grow
&lt;/h3&gt;

&lt;p&gt;Your RestlessIDE account gives you up to 3 shared workspaces per user. What happens if you need more than that, or if you need something with a more powerful CPU, more disk space, more memory, or even GPU access?&lt;/p&gt;

&lt;p&gt;You can get a Host!&lt;/p&gt;

&lt;p&gt;These Hosts have various CPU, memory, disk and GPU configurations meant to meet your needs.&lt;/p&gt;

&lt;p&gt;Do you have a bunch of different projects, but only really need to worry about a few at a time? You can get a small host with 4gb memory, fill it up with workspaces, and shut shut them down when they aren’t in use to limit memory usage.&lt;/p&gt;

&lt;p&gt;Do you lead a larger organization with many different groups, each with their own dev databases, caches and separate OpenProject installs? Get a large server, create all of the infrastructure and let them have at it. If your needs change, you can just shut down the services you don’t use or add new ones, and your bill doesn’t change. You’re renting the box and you can use it how you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Built for Teams
&lt;/h3&gt;

&lt;p&gt;RestlessIDE is made to help your team work more effectively together.&lt;/p&gt;

&lt;p&gt;When adding new members to your account, you can use Groups to define your various teams, and assign Admins to lead them. Admins have the ability to join workspaces of their team members check out their code, offer advice and guidance, and help them get unstuck.&lt;/p&gt;

&lt;p&gt;We also offer a growing number of open-source services to help your team collaborate more effectively, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://zulip.com/" rel="noopener noreferrer"&gt;Zulip&lt;/a&gt;: A collaboration tool with features similar to Slack, including group and video chats, direct messages, mobile clients for your phone, etc.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.openproject.org/" rel="noopener noreferrer"&gt;OpenProject&lt;/a&gt;: Project management software similar to Jira. Works with various project management paradigms, including classic, agile, and hybrid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These can be configured to run right on your Hosts free of charge, perhaps even saving you some money over subscriptions to alternative services.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Predictable Pricing
&lt;/h3&gt;

&lt;p&gt;I don’t know about you, but I’ve been burned by cloud pricing. It’s pitched as this thing where you “only pay for what you use!” which is technically true, but really only useful when the thing that you’re paying for is super-expensive and you want to be sure will only run for a short time.&lt;/p&gt;

&lt;p&gt;For 90% of development infrastructure you’ll want it running indefinitely, and you don’t want surprises when the bill comes each month. If you use PostgreSQL on all your projects, and your company survives more than a month, won’t you want a PostgreSQL dev database running the whole time? And don’t get me started with the arbitrary limitations. With RDS, for example, if you start a small instance, AWS limits the number of connections you can have to the server. Go over that (like if you’re in a connection pool environment, and have multiple developers running local servers) things just start breaking in weird ways. Who needs that?&lt;/p&gt;

&lt;p&gt;With RestlessIDE, you pay for 3 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The monthly subscription.&lt;/li&gt;
&lt;li&gt; Each user you add to your account.&lt;/li&gt;
&lt;li&gt; Each Host you add to your account.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s it. If you’re a solo entrepreneur with no teammates or Hosts, you only pay for yourself. If you have a team of 3 and are fine with the 3 workspaces per person, you pay for yourself and two additional users. If you decide to get a host, you can add a bunch of services or additional workspaces to it, delete them, add more, whatever, and you just pay for the Host. No need to deliberate before making changes to your own host. Use it how you want.&lt;/p&gt;

&lt;p&gt;Over time our selection of Services will only get better, and there will be more and more ways to get value out of your RestlessIDE account.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Not Going Anywhere
&lt;/h3&gt;

&lt;p&gt;I made this product because I really believe in browser-based software development.&lt;/p&gt;

&lt;p&gt;For several years I was a happy customer of Cloud9, an earlier web-based IDE, using it on my Chromebox and Chromebooks up until Amazon purchased it and (some time later) cut off access to the website, directing people into the bowels of AWS to run it from there. I tried. Believe me, I TRIED. They killed all of the things I loved about the service. Sure, you could still edit code in it, but if you wanted to see your running website? Set up Security Groups, and make sure your Egress and Ingress are set up right! Ugh.&lt;/p&gt;

&lt;p&gt;I then switched to [redacted]. It was a pretty cool service, using the Eclipse-based Theia editor when I started using it; the prebuilt environments were nice and it felt like Cloud9 used to. Then I had an episode where the site was unavailable for almost a week, and the Twitter-based support wasn’t offering timelines for when it would be back. Then they changed the interface, and made you jump through some hoops if you still needed to access workspaces on the old version of the site (I had about 30). Nowadays it seems like they are trying to rebrand as an AI-based development environment, with pricing based on the number of hours you use the workspaces. When you’re crunching on a deadline, do you really want to watch your hours to make sure you don’t need to pay extra for your developer tools? I’m sympathetic to the challenges of a startup trying to find a workable business plan and needing to try to tailor the product to what will get them funding, but as a paying customer I was frustrated.&lt;/p&gt;

&lt;p&gt;Since then I’ve previewed a few other services and found them all with weird limitations. Workspaces shut down after 30 minutes of activity regardless of what you tried to do to save them. Some services have different tiers of environments, with some being free and others (the ones with terminal access!) being very limited in terms of hours for the monthly price. Some of them used truly awesome technology; one I saw lets you clone workspaces almost instantaneously so that you can (I guess?) A-B test various ideas and solutions without delay. When I sat back and thought about it, though, it didn’t really match the way me and the teams I’ve been on worked. For a workspace that I intend to use for 3 months of work, I don’t really care if it takes 5 seconds or 30 seconds to initialize the first time I set it up.&lt;/p&gt;

&lt;p&gt;RestlessIDE is different because it’s not searching for a business model. I built it for myself and others like me, and I use it every day with my consulting practice. There aren’t investors I need to satisfy or arbitrary milestones I need to hit to make my hockey stick take off. I want it to grow organically, with features prioritized by the needs of customers. And I want to charge a fair, sustainable price so I can make sure the service can thrive.&lt;/p&gt;

&lt;p&gt;If you start using RestlessIDE today, you can be assured it will still be there tomorrow, serving up your code dutifully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now for the Fun Part
&lt;/h2&gt;

&lt;p&gt;It’s about to get interesting. My baby is entering the world and it’ll likely get kicked around a bit, like all babies (?) do. Maybe our childhoods were different. Anyway, it’s exciting but also a little scary.&lt;/p&gt;

&lt;p&gt;I hope you’ll join me for this journey. Please click the button below to sign up for our beta, and we’ll send you an email to try it out as soon as we have slots available. Everyone who participates in the beta will get a credit good for up to 2 months of free service!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://restlesside.com" rel="noopener noreferrer"&gt;Join the Beta&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>development</category>
      <category>software</category>
    </item>
    <item>
      <title>A Complete Noob’s Guide to Kubernetes</title>
      <dc:creator>Mike</dc:creator>
      <pubDate>Mon, 16 Jun 2025 18:35:17 +0000</pubDate>
      <link>https://forem.com/restlessmike/a-complete-noobs-guide-to-kubernetes-4e4j</link>
      <guid>https://forem.com/restlessmike/a-complete-noobs-guide-to-kubernetes-4e4j</guid>
      <description>&lt;p&gt;This article first appeared on the &lt;a href="https://restlessdev.com/2025/06/16/a-complete-noobs-guide-to-kubernetes/" rel="noopener noreferrer"&gt;RestlessDev blog&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;(Photo By &lt;a href="https://www.pexels.com/photo/green-peas-on-black-round-plate-4963963/" rel="noopener noreferrer"&gt;Kaboompics.com&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;So you’re interested in deploying your app to Kubernetes but just don’t know how to get started. Been there. I was like you once, 3 days ago.&lt;/p&gt;

&lt;p&gt;And now I’m qualified to tell you how to get it done. Well, semi-qualified.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Guide Is
&lt;/h2&gt;

&lt;p&gt;This guide is a crash course in telling you what you need to know to get started in Kubernetes for a typical web app. This includes getting the site running with traffic going to it, as well as setting up a worker process for running non-web tasks in the background and a database. It also covers some of the terminology you’ll need to know. I caution you to read what I say here with a grain of salt; I’m describing my experience while also attempting to simplify things where I can so that Mere Mortals can get started with this very complicated and full-featured platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Guide Is Not
&lt;/h2&gt;

&lt;p&gt;This guide won’t tell you how do deploy a Kubernetes cluster. This will vary a lot depending on which cloud provider you’re using, or if you’re trying to set it up on your own hardware. In my case I used &lt;a href="https://github.com/kube-hetzner/terraform-hcloud-kube-hetzner?tab=readme-ov-file" rel="noopener noreferrer"&gt;Kube-Hetzner&lt;/a&gt;, which was very easy to use once I read through the GitHub readme and set up the right instance types for my desired cluster. It also doesn’t cover the process of containerizing your application. I’ve used &lt;a href="https://podman.io/" rel="noopener noreferrer"&gt;Podman&lt;/a&gt; to build my images but it should work just as well with other containerization systems.&lt;/p&gt;

&lt;p&gt;It also doesn’t get into maintenance of your cluster. To be honest, I’ll be learning that as well, as I’m just setting out on this journey myself.&lt;/p&gt;

&lt;p&gt;Before you run, you need to crawl. This guide is will get you crawling like a grad student. That’s good, right? They have to be pretty fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Background
&lt;/h2&gt;

&lt;p&gt;Like others out there, I’ve been through many different iterations on what the current “best practice” (ick) is when deploying web apps. Bare-metal servers (or just “servers” as we used to call them) gave way to virtual machines, which then gave way to platforms like Heroku, which gave way to Amazon’s version of Heroku, Elastic Beanstalk. Then containerization hit, and every cloud provider had their own way to turn a Docker image into a running website. Recently I’ve been using Amazon’s ECS to deploy sites; it’s been pretty relable once it was up and running, but I wasn’t always crazy about the added costs that seemed to pop up all over the place. A load balancer was &lt;em&gt;how much?&lt;/em&gt; What’s a VPC and why is it so expensive? And don’t get me started about RDS pricing.&lt;/p&gt;

&lt;p&gt;Kubernetes was something I had heard about, but it seemed so enterprise-y. I’m just a simple country boy, I don’t have an army of consultants to deploy my sites and keep them running. I ran through the minikube tutorial a few years back and got something simple running locally, but to go from that to an actual &lt;em&gt;cluster&lt;/em&gt; seemed like a leap I wasn’t prepared to take.&lt;/p&gt;

&lt;p&gt;Until last Thursday.&lt;/p&gt;

&lt;p&gt;I’ve been working on something that just &lt;em&gt;begged&lt;/em&gt; for a Real Deployment, and I wanted to get off of the vendor lock-in treadmill I’ve been on the last few years. Kubernetes answered the call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminology
&lt;/h2&gt;

&lt;p&gt;The first thing that I noticed about Kubernetes is all of the terminology you have to learn. It’s a huge platform, built to handle all different types of workloads with a lot of customization available. Fortunately, you don’t need to learn every little thing just to get started.&lt;/p&gt;

&lt;p&gt;Here are the terms you really need to know.&lt;/p&gt;




&lt;h3&gt;
  
  
  Control Planes and Nodes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Control Planes&lt;/strong&gt; are the brains of your cluster, and are the thing you actually talk to when using the &lt;strong&gt;kubectl&lt;/strong&gt; command line interface. They generally don’t have to have a ton of resources, because they don’t run anything other than the API and other coordination and management software. &lt;strong&gt;Nodes&lt;/strong&gt; are the part of the cluster that actually run your containers, so the amount of memory and compute power (and storage) they have does impact the capacity of your cluster. Many cloud providers seem to provide their own &lt;strong&gt;Control Planes&lt;/strong&gt; free of charge as long as you use their &lt;strong&gt;Nodes&lt;/strong&gt;; if you’re already on their platform it could work out and provide one less thing for you to maintain, but just keep in mind that they aren’t generally very expensive instance types. This isn’t the reason to choose one platform over another, in my opinion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kubectl
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Kubectl&lt;/strong&gt; is the command-line tool used to talk to your cluster. It has a rather simple and consistent interface, and once you get used to it, it’s pretty easy to get things done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Namespaces
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Namespaces&lt;/strong&gt; are how Kubernetes sorts your various resources into logical buckets. In my case, I have a single namespace for my application and everything lives in it. There is a default namespace, but that’s how we get ants. Be a mensch and create a new namespace please.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployments
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Deployments&lt;/strong&gt; are how you instruct Kubernetes to turn your images into actual running containers, called &lt;strong&gt;Pods&lt;/strong&gt;. Each deployment can deploy multiple &lt;strong&gt;pods&lt;/strong&gt; with the same image if you need a load balancing situation. You can also use deployments to launch other services like databases and caching layers using their own images. You basically use &lt;strong&gt;deployments&lt;/strong&gt; to describe what you want the cluster to look like, and then Kubernetes makes sure it happens.&lt;/p&gt;

&lt;p&gt;Your running application will consist of several different &lt;strong&gt;Deployments&lt;/strong&gt;; in my case I have one for my application API, one for my application cron runner, one for my database, and one for memcached.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pods
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;“Pods”&lt;/strong&gt; are Kubernetes-speak for running containers. You can connect to them directly to get a command-line interface for debugging, and can access their logs to see whatever information you want. They should generally be treated as ephemeral and non-permanent, outside of any &lt;strong&gt;Volumes&lt;/strong&gt; that they use for persistent storage. The Kubernetes platform will replace &lt;strong&gt;Pods&lt;/strong&gt; with fresh versions whenever it needs to to maintain the integrity of the &lt;strong&gt;Deployments&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Volumes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Volumes&lt;/strong&gt; are used to provide a place to store data that needs to persist over time. They get attached to a &lt;strong&gt;Pod&lt;/strong&gt; (or to several &lt;strong&gt;Pods&lt;/strong&gt; if you want) and will live beyond that &lt;strong&gt;Pod&lt;/strong&gt;‘s life cycle. For things like databases this is essential.&lt;/p&gt;

&lt;h3&gt;
  
  
  ConfigMaps and Secrets
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ConfigMaps&lt;/strong&gt; and &lt;strong&gt;Secrets&lt;/strong&gt; are Kubernetes’ way of dealing with environment variables. &lt;strong&gt;ConfigMaps&lt;/strong&gt; are a little easier to deal with because they are unencrypted and can be edited directly, so use them for environment variables that aren’t sensitive. &lt;strong&gt;Secrets&lt;/strong&gt; are stored differently internally, but are unencrypted by Kubernetes and sent to &lt;strong&gt;Pods&lt;/strong&gt; as regular environment variables when needed. They are good for putting things like API keys that you want to keep safe. You can have as many &lt;strong&gt;ConfigMaps&lt;/strong&gt; and &lt;strong&gt;Secrets&lt;/strong&gt; as you want, each with their own name; they are assigned to the &lt;strong&gt;Deployment&lt;/strong&gt; in the &lt;strong&gt;Deployment&lt;/strong&gt;‘s YAML file. In my case, my API and cron images are based on the same codebase, so I use the same &lt;strong&gt;ConfigMap&lt;/strong&gt; and &lt;strong&gt;Secret&lt;/strong&gt; for both.&lt;/p&gt;

&lt;p&gt;If your images are in a private repository, you’ll also need a &lt;strong&gt;Secret&lt;/strong&gt; to tell Kubernetes how to access them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Services
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Services&lt;/strong&gt; are how you map ports within your &lt;strong&gt;Pods&lt;/strong&gt; to the outside world. In my case I have a &lt;strong&gt;Service&lt;/strong&gt; for my API but not for the cron runner, which just sorta does its thing in the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ingress
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Ingress&lt;/strong&gt; is a layer on top of &lt;strong&gt;Services&lt;/strong&gt; specifically for HTTP/HTTPS connections. In my case, it’s used for higher-level functions like getting certificates from Let’s Encrypt. You can also use it to route different paths to different &lt;strong&gt;Services&lt;/strong&gt; and things like that, although I don’t use that.&lt;/p&gt;

&lt;h3&gt;
  
  
  ClusterIssuer
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ClusterIssuer&lt;/strong&gt; is used when registering SSL certificates.&lt;/p&gt;




&lt;p&gt;That’s it. Those are the only terms you need to know to deploy an application on Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying an Application
&lt;/h2&gt;

&lt;p&gt;Do you like YAML? I hope so, because you’re going to be using it a lot. Let’s dive in.&lt;/p&gt;

&lt;p&gt;Throughout this tutorial, I’ll use the following placeholders to indicate things you should replace with your own variation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &amp;lt;namespace&amp;gt;: The namespace of the application. To make it simple, just try to use a standard lowercase slug-type string. my-namespace would be fine.&lt;/li&gt;
&lt;li&gt;  &amp;lt;app-name&amp;gt;: Kubernetes lets you assign each resource a name which needs to be unique within a namespace. For simplicity, I call my deployments things like my-app-api, my-app-cron, and my-app-db and my services things like my-app-api-service. You’re free to call yours whatever you want, but if you call yours something different just make sure to put the right name in the right place.&lt;/li&gt;
&lt;li&gt;  &amp;lt;*-username&amp;gt; and &amp;lt;*-password&amp;gt;: You may need to reference different usernames and passwords in your code. I’ll replace the * with an identifier to help you know what’s expected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’d suggest creating a directory that you’ll use for storing all of your YAML files as you’ll want to reapply them whenever you make changes. Throughout the tutorial I’ll assume you’re in the directory with all of your files.&lt;/p&gt;

&lt;p&gt;It’s also worth noting that the names of your YAML files aren’t important, but the content in them is. I happen to call them the same name as the resources they reference, but you might go a different way. It’s cool, we can still be friends.&lt;/p&gt;




&lt;p&gt;Side note: We’re going to be using kubectl a lot below, and kubectl needs to know how to talk to your cluster. In my case, my deployment process output a file called &lt;em&gt;kubeconfig.yaml&lt;/em&gt; that had all of the relevant information. This file can either be supplied to kubectl as an argument (&lt;em&gt;–kubeconfig&lt;/em&gt;) or by setting an environment variable (&lt;strong&gt;KUBECONFIG&lt;/strong&gt;). I’ve opted for the latter, which is why you won’t see this important detail in all the examples.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Create the Namespace
&lt;/h3&gt;

&lt;p&gt;First you’ll need to create your namespace. This is the bucket where everything else will live. To do this, make the following YAML:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;namespace.yaml&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: v1
kind: Namespace
metadata:
  name: &amp;lt;namespace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now apply this YAML to your cluster with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f namespace.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re under way!&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Deploy your Database
&lt;/h3&gt;

&lt;p&gt;In this example, I’m using &lt;a href="https://cloudnative-pg.io/" rel="noopener noreferrer"&gt;CloudNative-PG&lt;/a&gt;, a Kubernetes-oriented PostgreSQL distribution that has lots of nice features built in.&lt;/p&gt;

&lt;p&gt;First we need to create the &lt;strong&gt;Secret&lt;/strong&gt; to store your database credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&amp;lt;app-name&amp;gt;-db-secret.yaml&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: v1
kind: Secret
metadata:
  name: &amp;lt;app-name&amp;gt;-db-secret
  namespace: &amp;lt;namespace&amp;gt;
type: Opaque
stringData:
  username: &amp;lt;database-user&amp;gt;
  password: &amp;lt;database-password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please remember to quote your password if it contains characters that make YAML unhappy.&lt;/p&gt;

&lt;p&gt;Apply it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f &amp;lt;app-name&amp;gt;-db-secret.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you should be able to deploy the database. You know the drill, another YAML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&amp;lt;app-name&amp;gt;-db.yaml&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: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: &amp;lt;app-name&amp;gt;-db
  namespace: &amp;lt;namespace&amp;gt;
spec:
  instances: 1
  primaryUpdateStrategy: unsupervised
  storage:
    storageClass: hcloud-volumes
    size: 20Gi
  bootstrap:
    initdb:
      database: &amp;lt;database-name&amp;gt;
      owner: &amp;lt;database-user&amp;gt;
      secret:
        name: &amp;lt;app-name&amp;gt;-db-secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing to notice is that the kind of this YAML file is Cluster. This goes a little beyond the scope of this tutorial, but my understanding is that you can create your own specs for YAML files that extend beyond the build in ones, as done here with the apiVersion referring to that CloudNative PG spec.&lt;/p&gt;

&lt;p&gt;Now apply it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f &amp;lt;app-name&amp;gt;-db.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So one thing about databases is that you don’t really want them to be easy for bad guys to get to. With this deployment the database is running inside your cluster without an external IP address.&lt;/p&gt;

&lt;p&gt;It so happens that you occasionally need to connect to your database, either to set it up initially, or maintain it, or just to run queries or updates. See how it’s doing. For this purpose we’ll want to set up a separate SSH host that we can use to create a tunnel into the database. This SSH server is typically called an SSH Bastion, I think because it sounds cool and a little Medieval. But also because it is a safe place to enter your private cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ssh-bastion.yaml&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: apps/v1
kind: Deployment
metadata:
  name: ssh-bastion
  namespace: &amp;lt;namespace&amp;gt;
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ssh-bastion
  template:
    metadata:
      labels:
        app: ssh-bastion
    spec:
      containers:
      - name: sshd
        image: linuxserver/openssh-server
        env:
        - name: PUID
          value: "1000"
        - name: PGID
          value: "1000"
        - name: TZ
          value: "UTC"
        - name: PASSWORD_ACCESS
          value: "true"
        - name: SUDO_ACCESS
          value: "true"
        - name: DOCKER_MODS
          value: "linuxserver/mods:openssh-server-ssh-tunnel"
        - name: USER_NAME
          value: &amp;lt;bastion-user&amp;gt;
        - name: USER_PASSWORD
          value: &amp;lt;bastion-password&amp;gt;
        ports:
        - containerPort: 2222
---
apiVersion: v1
kind: Service
metadata:
  name: ssh-bastion
  namespace: &amp;lt;namespace&amp;gt;
spec:
  selector:
    app: ssh-bastion
  ports:
  - port: 2222
    targetPort: 2222.
  type: LoadBalancer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have noticed we cheated a bit here and had both a deployment &lt;em&gt;and&lt;/em&gt; a service defined in the same YAML file. You can do that if you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f ssh-bastion.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give it a minute to deploy, and let’s check out our progress.&lt;/p&gt;

&lt;p&gt;First, let’s see our pods:&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 pods -n &amp;lt;namespace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see one for your database and another for your SSH bastion. Great work!&lt;/p&gt;

&lt;p&gt;Now if we want to actually use our bastion, we need to figure out its external IP. To do that we need to look up the services.&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 svc -n &amp;lt;namespace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a line for ssh-bastion with a value in the External-IP column. Grab that number.&lt;/p&gt;

&lt;p&gt;Open up another terminal window and enter this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no -p 2222 -N -L &amp;lt;open-local-port&amp;gt;:&amp;lt;app-name&amp;gt;-db-rw.&amp;lt;namespace&amp;gt;.svc.cluster.local:5432 &amp;lt;bastion-user&amp;gt;@&amp;lt;bastion-external-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So yeah, there are a lot of placeholders there.  should be any local port that you will use to connect to your cluster’s database. I’m assuming you’re already running PostgreSQL on your local machine. If you aren’t, 5432 is fine.  is that IP address I told you to grab earlier. The other placeholders you should already have.&lt;/p&gt;

&lt;p&gt;As you’ll see, Kubernetes does a lot of magic with DNS inside your cluster. In this case, it creates a hostname for your read-write database at &amp;lt;app-name&amp;gt;-db-rw. Remember that for later when it comes to setting up your application’s &lt;strong&gt;ConfigMaps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once you run this command, you’ll need to enter your . It’ll look like your terminal hung. It hasn’t, it’s just opening up a tunnel for you and keeping it open.&lt;/p&gt;

&lt;p&gt;Now you can go to your preferred PostgreSQL client and connect to your cluster database with the following parameters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Hostname: localhost&lt;/li&gt;
&lt;li&gt;  Port: &amp;lt;open-local-port&amp;gt;&lt;/li&gt;
&lt;li&gt;  Username: &amp;lt;database-user&amp;gt;&lt;/li&gt;
&lt;li&gt;  Password: &amp;lt;database-password&amp;gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can set up your application’s database and do any other configuration you need. When you’re done, kill that SSH tunnel and you’re good to go!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Deploy the Application
&lt;/h3&gt;

&lt;p&gt;Now that we have the database working as planned, we can turn to the application.&lt;/p&gt;

&lt;p&gt;My application consists of two parts, the API and a separate process that runs cron jobs on a regular schedule to monitor my system and tidy things up when needed. The actual website for my application is hosted outside of Kubernetes; it’s an SPA that will connect to this API to do its work.&lt;/p&gt;

&lt;p&gt;Since the API and cron runner both share the same codebase, they have the same environment variables. Step 1 is to add the &lt;strong&gt;ConfigMap&lt;/strong&gt; and &lt;strong&gt;Secret&lt;/strong&gt; that the site code will use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&amp;lt;app-name&amp;gt;-config.yaml&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: v1
kind: ConfigMap
metadata:
  name: &amp;lt;app-name&amp;gt;-config
  namespace: &amp;lt;namespace&amp;gt;
data:
  NODE_ENV: production
  LOG_LEVEL: info
  APP_DB_HOST: &amp;lt;app-name&amp;gt;-db-rw
  APP_DB_PORT: "5432"
  APP_DB_NAME: &amp;lt;database-name&amp;gt;
  PORT: "3000"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&amp;lt;app-name&amp;gt;-secret.yaml&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: v1
kind: Secret
metadata:
  name: &amp;lt;app-name&amp;gt;-secret
  namespace: &amp;lt;namespace&amp;gt;
type: Opaque
stringData:
  APP_DB_USERNAME: &amp;lt;database-user&amp;gt;
  APP_DB_PASSWORD: &amp;lt;database-password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now apply each of these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f &amp;lt;app-name&amp;gt;-config.yaml
kubectl apply -f &amp;lt;app-name&amp;gt;-secret.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Quick Detour
&lt;/h4&gt;

&lt;p&gt;Do you use a private image repository to store your images? If so you need to tell Kubernetes how to access it. This can be accomplished through adding a Secret. We’ll do it inline this time, as the Docker Hub YAML format is a little wonky. (For those interested, you need to base64-encode a JSON blob and place it in the YAML)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create secret docker-registry dockerhub-secret \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=&amp;lt;docker-username&amp;gt; \
  --docker-password=&amp;lt;docker-access-token&amp;gt; \
  --docker-email=&amp;lt;docker-email&amp;gt; \
  --namespace=&amp;lt;namespace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;With those in place, we’ll start by deploying the cron worker. (Remember to remove the imagePullSecrets key if you aren’t authenticating to Docker Hub)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&amp;lt;app-name&amp;gt;-cron.yaml&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: apps/v1
kind: Deployment
metadata:
  name: &amp;lt;app-name&amp;gt;-cron
  namespace: &amp;lt;namespace&amp;gt;
spec:
  replicas: 1
  selector:
    matchLabels:
      app: &amp;lt;app-name&amp;gt;-cron
  template:
    metadata:
      labels:
        app: &amp;lt;app-name&amp;gt;-cron
    spec:
      imagePullSecrets:
      - name: dockerhub-secret
      containers:
      - name: cron
        image: docker.io/&amp;lt;docker-user&amp;gt;/&amp;lt;app-name&amp;gt;-cron:&amp;lt;tag-name&amp;gt;
        envFrom:
        - configMapRef:
            name: &amp;lt;app-name&amp;gt;-config
        - secretRef:
            name: &amp;lt;app-name&amp;gt;-secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f &amp;lt;app-name&amp;gt;-cron.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we’ll deploy the API:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&amp;lt;app-name&amp;gt;-api.yaml&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: apps/v1
kind: Deployment
metadata:
  name: &amp;lt;app-name&amp;gt;-api
  namespace: &amp;lt;namespace&amp;gt;
spec:
  replicas: 2
  selector:
    matchLabels:
      app: &amp;lt;app-name&amp;gt;-api
  template:
    metadata:
      labels:
        app: &amp;lt;app-name&amp;gt;-api
    spec:
      imagePullSecrets:
      - name: dockerhub-secret
      containers:
      - name: api
        image: docker.io/&amp;lt;docker-user&amp;gt;/&amp;lt;app-name&amp;gt;-api:&amp;lt;tag-name&amp;gt;
        ports:
        - containerPort: 3000
        envFrom:
        - configMapRef:
            name: &amp;lt;app-name&amp;gt;-config
        - secretRef:
            name: &amp;lt;app-name&amp;gt;-secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll notice that we have 2 replicas specified in this &lt;strong&gt;deployment&lt;/strong&gt;; this will launch 2 different &lt;strong&gt;pods&lt;/strong&gt; and then load balance them for us for a little redundancy. In this example, our API publishes at port 3000 within the container. You may want to replace this (and other 3000s within this tutorial) with your own value if you do it differently.&lt;/p&gt;

&lt;p&gt;And apply it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f &amp;lt;app-name&amp;gt;-api.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because this set of &lt;strong&gt;Pods&lt;/strong&gt; will need to listen on a port, we’ll need to add a service. We can do that like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&amp;lt;app-name&amp;gt;-api-service.yaml&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: v1
kind: Service
metadata:
  name: &amp;lt;app-name&amp;gt;-api-service
  namespace: &amp;lt;namespace&amp;gt;
spec:
  selector:
    app: &amp;lt;app-name&amp;gt;-api
  ports:
    - protocol: TCP
      port: 3000      # The port exposed by the service
      targetPort: 3000  # The port your app listens on inside the container
  sessionAffinity: ClientIP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And apply it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f &amp;lt;app-name&amp;gt;-api-service.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Ingress and Domain Pointing
&lt;/h3&gt;

&lt;p&gt;So our site is all deployed. Now we just have to make it visible to the world.&lt;/p&gt;

&lt;p&gt;In my case I needed to do two things to make this happen. First, I needed to set up the &lt;strong&gt;ClusterIssuer&lt;/strong&gt; for my cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;certissuer-prod.yaml&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:
    email: &amp;lt;email-address&amp;gt;
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
      - http01:
          ingress:
            class: traefik
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f certissuer-prod.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we need to add the Ingress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ingress.yaml&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: networking.k8s.io/v1
kind: Ingress
metadata:
  name: &amp;lt;app-name&amp;gt;-ingress
  namespace: &amp;lt;namespace&amp;gt;
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  rules:
  - host: api.&amp;lt;app-domain&amp;gt;.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: &amp;lt;app-name&amp;gt;-api-service
            port:
              number: 3000
  tls:
    - hosts:
        - api.&amp;lt;app-domain&amp;gt;.com
      secretName: api-&amp;lt;app-domain&amp;gt;-tls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f ingress.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whew, we’re finally almost done. Just one more step: We need to point our domain to the new site. First, though, we need an IP address to point it to.&lt;/p&gt;

&lt;p&gt;Run the following command:&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 ingress -n &amp;lt;namespace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the Address column, you should see an IP address. Point an A record at that thang and you’re in business!&lt;/p&gt;

&lt;h2&gt;
  
  
  Some More Tips
&lt;/h2&gt;

&lt;p&gt;That wasn’t so bad, was it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kubectl&lt;/strong&gt; is also really easy to use once you get the hang of it. For the most part, you can get lists on any part of your cluster like this:&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 &amp;lt;entity&amp;gt; -n &amp;lt;namespace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where  can be pods, configmaps, secrets, deployments, svc or whatever. If you want to inspect something on that list, you can use describe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl describe &amp;lt;entity&amp;gt; &amp;lt;entity-name&amp;gt; -n &amp;lt;namespace&amp;gt;

# example
kubectl describe pod my-app-api-iwuqe8 -n my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When working with &lt;strong&gt;pods&lt;/strong&gt;, you can view their logs like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl logs &amp;lt;pod-name&amp;gt; -n &amp;lt;namespace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that &lt;strong&gt;pods&lt;/strong&gt; are not the same as &lt;strong&gt;deployments&lt;/strong&gt;; you’ll need to get your actual &lt;strong&gt;pod&lt;/strong&gt; instance names from kubectl get pods to connect to the specific instances for logs, exec, and other pod-specific things.&lt;/p&gt;

&lt;p&gt;You can get a bash shell on a &lt;strong&gt;pod&lt;/strong&gt; with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl exec -it &amp;lt;pod-name&amp;gt; -n &amp;lt;namespace&amp;gt; --  bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If necessary, you can also replace bash above with any other command that’s available on your &lt;strong&gt;pod&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you want to make a small edit to a &lt;strong&gt;ConfigMap&lt;/strong&gt;, you can do it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl edit configmap &amp;lt;configmap-name&amp;gt; -n &amp;lt;namespace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use a similar syntax for other entities, if needed.&lt;/p&gt;

&lt;p&gt;You’ll also occasionally find yourself needing to restart your pods after you update a &lt;strong&gt;ConfigMap&lt;/strong&gt; or &lt;strong&gt;Secret&lt;/strong&gt;, or perhaps pushed out an update to your image (if you use the latest tag rather than versioning each release, for example) that you want to have reflected in your &lt;strong&gt;Pods&lt;/strong&gt;. No problemo. You can do a patch to your &lt;strong&gt;pod&lt;/strong&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl patch deployment &amp;lt;deployment-name&amp;gt; -n &amp;lt;namespace&amp;gt; \ 
  -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"configmap-reload-timestamp\":\"$(date +%s)\"}}}}}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’ve pushed up a new tag version of your image and you’d like to patch your deployments to use it, you can use this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl set image deployment/&amp;lt;app-name&amp;gt;-api api=docker.io/docker.io/&amp;lt;docker-user&amp;gt;/&amp;lt;app-name&amp;gt;-api:&amp;lt;new-tag&amp;gt; -n &amp;lt;namespace&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’d like to update your configuration after changing any of your YAML files, you can also just reapply your updates using kubectl apply -f like you have been. Just make sure that you keep track of any changes you’ve made via edits or patches to make sure you don’t apply an older version of your configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whew!
&lt;/h2&gt;

&lt;p&gt;Ok, that should be enough for now. Hopefully it wasn’t too overwhelming.&lt;/p&gt;

&lt;p&gt;I’ve found that Kubernetes way of conceptualizing an application, once I internalized it, was actually easier than ECS. Each step served a particular purpose, and there wasn’t a lot of unnecessary boilerplate needed just to get things set up. Hopefully you’ll have a similar experience.&lt;/p&gt;

&lt;p&gt;Til next time.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>kubernetes</category>
      <category>docker</category>
      <category>api</category>
    </item>
  </channel>
</rss>
