enhost Blogs

Global Reader

Welcome to the Global Reader, your window into the vibrant community of enhost BLOGS! Here, you'll find a dynamic feed of the latest posts from all our users, showcasing the diverse voices and perspectives within our Fediverse network. Explore fresh ideas, discover new writers, and engage with content that sparks your curiosity. Dive in and see what the enhost BLOGS community is creating today!

from Dev log //HCAZ

Like many developers who have spent over 15 years working with PHP, I have watched the frontend landscape shift dramatically. There is always a new “must-use” framework, and lately, the static site generation (SSG) space has been dominated by JavaScript solutions like Astro, Next.js, or Gatsby.

For a long time, I felt the pressure to jump ship. If I wanted the speed and security of a static site, I assumed I had to write it in JavaScript. But when I actually sat down to build my latest project, I realized something important: I didn't want to learn a whole new ecosystem just to output HTML. I wanted to use the tools I have mastered over the last decade and a half.

That is when I found Jigsaw.

The Comfort of “Home”

As a Laravel developer, my brain is wired for Blade templating. It is efficient, expressive, and I know it inside out. Switching to a JS-based generator meant fighting against muscle memory. I would have to relearn how to loop through data, how to structure partials, and how to handle logic in the view layer.

Jigsaw changed that equation. It is a framework for building static sites using the exact same tools I use for complex web applications.

The biggest win for me was Blade. Being able to use Blade syntax for a static site felt like a cheat code. I could build layouts, components, and includes exactly how I would in a full Laravel app, but without the complexity of a full backend. I wasn't fighting the tool; I was just writing code.

Simplicity in Deployment

The other major appeal of going static is the deployment story.

When running a traditional PHP application, you have to worry about the overhead. You need to manage PHP-FPM, configure Nginx or Apache correctly, worry about database connections, and keep everything patched.

With a static site generated by Jigsaw, all that overhead vanishes. The build process runs locally, and the output is just simple HTML, CSS, and assets.

This makes hosting incredibly simple. I don't need a complex server stack. I can throw the files into an S3 bucket or serve them with Caddy. Caddy is particularly great here because it handles SSL automatically and serves static files with zero fuss. It is “set and forget” infrastructure, which is exactly what I need when I am juggling multiple projects.

Use What You Know

There is a tendency in our industry to chase the newest shiny object. While Astro and others are fantastic tools, they weren't the right tools for me.

Choosing Jigsaw wasn't about stubbornness. It was about pragmatism. By sticking to PHP and Blade, I removed the cognitive load of learning a new syntax and focused purely on shipping the content.

If you are a PHP veteran looking at the static site world and feeling out of place, look at Jigsaw. You don't have to leave your skills at the door to build modern, fast websites.

 
Read more...

from The Pragmatic Pixel

Hey everyone, Jamie here.

I’m going to be honest with you. I sat down to write this post about two hours ago.

Since then, I have: made a coffee, adjusted the height of my monitor by 2 millimeters, read the release notes for a PHP version I’m not even using yet, and thoroughly dusted my mechanical keyboard.

It’s ironic, isn’t it? We build systems designed to optimize efficiency, automate workflows, and process data in milliseconds. Yet, as developers, we are often subject to the most inefficient, buggy, and unpredictable operating system of all: the human brain.

Procrastination is the silent killer of productivity in our industry. But lately, I’ve stopped viewing it as a character flaw and started viewing it as a system signal.


It’s Not Laziness, It’s Complexity

When I look at why I’m procrastinating, it’s rarely because I don’t want to work. I love coding. I love building things.

Usually, the procrastination hits hardest when I’m facing a task that is high in ambiguity or high in risk.

Take the legacy monolith I talked about in my recent series. Staring at a 2,000-line file of spaghetti code trying to figure out where to insert a proxy route is terrifying. The cognitive load required just to load that context into my head is immense. My brain sees that mountain and says, “No thanks, let's organize the Downloads folder instead. That’s safe. That has a clear definition of 'done'.”

In photography (my other great love), I feel the same friction. Taking photos is easy; it’s the flow state. But sitting down to edit 500 RAW files? That’s daunting. So I put it off.

We procrastinate to protect ourselves from the discomfort of the unknown.

The Danger of “Productive” Procrastination

The worst kind of procrastination for a developer is the kind that looks like work. This is the “Yak Shaving” trap.

  • The IDE Theme: “I can't possibly start this complex API integration until I've found a color scheme that has slightly better contrast on PHP attributes.”
  • The Refactor: “I know I need to build the checkout feature, but look at this unrelated helper function! It’s messy! I’ll just clean this up first...”
  • The Tooling: “Maybe if I switch from Bash to Zsh and configure a bunch of new aliases, I’ll be 10% faster at typing the code I’m currently avoiding writing.”

We trick ourselves into feeling productive because we are doing something technical. But we aren't moving the needle. We're just spinning our wheels.


How I Trick My Brain Into Starting

Over the years, running my own company and working in various teams, I've developed a few pragmatic strategies to bypass the “loading spinner” in my head.

1. The “Hello World” of the Task

If a task is too big, I won't start it. So, I lower the bar until it’s comically easy.

If the task is “Build the User Dashboard,” that’s too scary. I change the task to: “Create a new blank controller and return 'Hello'.”

That’s it. I can do that in 30 seconds. Once I’ve done that, the friction is gone. I’m in the file. I might as well add the route. Then I might as well fetch the data. Momentum is everything.

2. Isolate the Fear

If I find myself avoiding a task, I stop and ask: “What specifically am I afraid of here?”

Usually, the answer is “I don't know how this library works” or “I'm scared I'll break the production database.” Once I identify the blocker, I can solve that. If I don't know the library, the task becomes “Read the docs for 10 minutes.” If I'm scared of breaking production, the task becomes “Write a test case.”

3. The 10-Minute Rule

I tell myself I only have to work on the thing for 10 minutes. If I want to stop after 10 minutes and go back to staring out the window, I can.

Spoiler alert: I never stop. The hardest part of a rocket launch is the lift-off. Once you're in orbit, it requires very little fuel to keep going.


Forgive Yourself

Finally, it’s important to remember that we aren't machines. Some days, the code just doesn't flow. Some days, the brain fog wins. And that’s okay.

Beating yourself up about procrastination usually just leads to guilt, which leads to stress, which leads to... more procrastination. It’s a vicious loop.

If you’re stuck today, close the laptop. Go for a walk. Switch contexts. The code will still be there tomorrow, and you’ll likely attack it with a fresh perspective.

Now, if you’ll excuse me, I have a “Hello World” controller I need to write.

Cheers,

Jamie C

 
Read more...

from The Pragmatic Pixel

Hey everyone, Jamie here.

This is the final part of my series on joining a new company with an established team and a legacy application.

In Part 1, I focused on the human element: the critical importance of listening, building trust, and respecting the team's deep-seated knowledge.

In Part 2, I dove into the code itself, treating the decades-old monolith not as a mess, but as a historical artifact—a “technical archaeology” project to map its structure and understand why it was built the way it was.

Now, I've arrived at the most delicate moment of all. I have the team's trust, and I have a functional map of the system. The business wants to build new, modern features. We, the dev team, want to use modern tools and practices. But the monolith is brittle, slow to change, and has no test coverage.

What do we do?


The Fantasy vs. The Reality

Every developer, upon seeing a legacy system, has the same fantasy: “Let's just rewrite the whole thing!”

We dream of wiping the slate clean, spinning up a fresh Laravel 11 project, and rebuilding everything “the right way” over six months. We present this to the business, they get excited, and we get to work.

This fantasy is a trap. It is, in my opinion, almost always the wrong answer.

The “Big Bang Rewrite” is a recipe for professional disaster. It dramatically underestimates the millions of tiny business rules hidden in the legacy code (the very rules I was discovering in Part 2). The project invariably takes three times as long and delivers half the functionality. By the time it's “finished,” the business has been treading water for two years and the new system is already out of date.

You can't just stop the business. The monolith is a critical, revenue-generating asset. You can't turn it off.

So, you don't rewrite it. You evolve it.

The Strategy: The Strangler Fig Pattern

The solution I've started to discuss with my new team is a well-known strategy, most famously named by Martin Fowler: The Strangler Fig Pattern.

The metaphor is simple and brilliant. A strangler fig starts as a small vine on an old, established tree. It grows, wrapping itself around the host. It gets stronger, puts down its own roots, and thickens. Eventually, after many years, the old tree inside rots away completely, leaving a new, healthy, hollow tree in the shape of the original.

This is our technical roadmap. We don't touch the old monolith. We build around it, piece by piece, until it's no longer needed.

Here’s the pragmatic, step-by-step plan I've proposed.

Step 1: Build the “Proxy” (The Trellis)

First, we need to control the flow of traffic. Right now, all traffic hits the old monolith directly. We need to put a “facade” or “proxy” in front of it.

This is the only piece of infrastructure we need to add. All user traffic will now hit this proxy first. Initially, its only job is to pass 100% of the traffic straight to the monolith. For the user, absolutely nothing has changed.

But for us, everything has changed. We now have a central control point.

(You all know my love for Caddy. It's the perfect tool for this. We can configure it to act as a reverse proxy, forwarding all requests to our monolith. It's clean, simple, and handles the automatic HTTPS we need.)

Step 2: Pick Our First “Vine” (A New Feature)

The business wants to build a new feature. Let's say it's a new, modern “User Dashboard.”

Great. We do not build this inside the monolith.

We spin up a brand-new, clean, modern Laravel 11 application. This is our first “vine.” We build this new dashboard the “right way”—with proper tests, DTOs, modern PHP, and a clean architecture. It's a self-contained application. It might even have its own database, or (more likely) it will safely read data from the monolith's database (but not write to it!).

This is a huge win. The team gets to work in a modern environment, the feature gets built quickly, and it's fully tested.

Step 3: Update the Proxy (The First “Strangle”)

Once the new feature is ready, we make one tiny change to our proxy's configuration. We add a rule that says 'any traffic for the new dashboard's URL should be routed to our new, modern Laravel app.' Then, we add a second rule that says 'all other traffic should continue to go to the old monolith, just as it always has.'

And just like that, we've started the process.

The user has no idea. They go to an old page and get the monolith. They click “Dashboard” and are seamlessly routed to our brand-new Laravel app. The two systems are living side-by-side, appearing as one.


The Long-Term Payoff: How We Win

This is where the magic happens. We got a quick win by shipping a new feature. Now, we repeat the process.

  • Next up: We decide to rewrite the “Contact Us” form (the one I traced in Part 2).
  • We build it: We add the new contact form logic to our modern Laravel application.
  • We update the proxy: We add a new, specific rule to send traffic for the 'Contact Us' page to the new app, while everything else continues to go to the monolith.

We continue this, one piece at a time. We “strangle” the old application, one route at a time. We can prioritise based on business value. The most critical, complex parts (like the order processing I found) can be left for last, once we've built our confidence and our new app is stable.

This approach is pragmatic and respectful. * It reduces risk. We're only changing one small thing at a time. If the new “Contact Us” form has a bug, we just remove that one routing rule from the proxy to instantly roll back. * It delivers value now. The business doesn't have to wait two years for a rewrite. They get their new dashboard next month. * It's great for the team. We get to build new, greenfield code immediately, which boosts morale. New hires can be pointed to the new, clean Laravel app, making onboarding a thousand times easier. * It's safe. The old monolith, the heart of the business, keeps running, untouched and stable, for as long as it needs to.

This is the long game. It's not a quick, dramatic victory. It's a slow, steady, and safe evolution. It’s the only way to turn a 20-year-old application into a modern one without stopping the very business it exists to serve.

It’s the pragmatic way.

Cheers, Jamie C

 
Read more...

from The Pragmatic Pixel

Hey everyone, Jamie here.

In Part 1, I talked about the human side of joining a small, tight-knit development team. My first week was all about building trust, listening more than talking, and showing respect for the team's history. I've fixed a few minor bugs, learned the deployment script, and I'm starting to understand the “why” behind their workflow.

Now, with that initial social foundation in place, it's time for the next, far more daunting task: understanding the codebase.

This isn't a modern, greenfield Laravel project. This is a “living fossil.” It's an application that has been successfully running and evolving since before “framework” was a common word in the PHP world. We're talking code with history—decades of it.

This process isn't software development in the modern sense. It's technical archaeology.


The First Look: “Where Is Everything?”

When you first git clone a modern project, you have a map. You have a composer.json to see dependencies, a routes/web.php to see entry points, and an app/Http/Controllers folder to see the logic. You can orient yourself in minutes.

Opening this project felt... different. There's no composer.json. The entry point is an index.php in the root directory, filled with a sprawling switch statement based on a $_GET parameter. Dependencies are managed by include_once statements. Logic and HTML are woven together in the same files.

My first reaction wasn't judgement. It was a genuine, slightly awestruck, “Wow. How do I even start?”

This code is from a different era. It was written before PSR standards, before namespaces, before modern OOP was the default. And most importantly, it works. It has successfully run the core of this business for years. My job isn't to mock it; it's to understand it.

To do that, I've had to throw out the modern playbook and adopt a new, forensic approach.

Strategy 1: Follow the Thread

You can't understand a 100,000-line monolith all at once. You have to trace a single path, like following one thread in a massive, tangled tapestry.

My approach was simple: I picked the most basic, non-critical feature I could find—the “contact us” form.

I opened the contact page in my browser, looked at the URL (/index.php?page=contact), and found the corresponding case 'contact': in the main index.php file. From there, I followed the include statements, file by file.

  • I found the file that rendered the HTML form.
  • I found the if (isset($_POST['submit'])) block that handled the submission.
  • I traced the variables as they were built into an email and sent using the old mail() function.

This simple, 20-minute exercise was a breakthrough. It taught me the fundamental architecture: how a request is routed, how page logic is included, and how form data is processed. I didn't understand the whole application, but I understood one complete slice of it.

Strategy 2: Follow the Money

After tracing a simple path, the next step is to trace the most important path. In any business, that means following the money. Whatever the core function is—processing a sale, generating a report, (or in my past life, aggregating supplier data)—that's the code you need to find.

This is the “sacred” part of the application. It's the most complex, the most critical, and the most feared.

I asked the team, “If I wanted to see how a new order is processed, where would I look?”

This led me to a 2,000-line file called process_order.php. This file is the heart of the beast. It has logic for validating stock, talking to a payment gateway (via a cURL request), inserting records into half a dozen database tables, and sending customer emails.

This file is a history book. You can see comments left by developers who haven't worked here in ten years. You can see blocks of code commented out after a business pivot. It's terrifying, but it's also the single most important piece of code in the company. By reading it, I'm not just learning the code; I'm learning the last 15 years of the company's business logic.

Strategy 3: Map the Database

The final piece of the puzzle is the database. The database schema is the Rosetta Stone for a legacy application.

While the PHP code might be a tangled mess of logic, the database schema is the source of truth. It shows the state.

I spent a whole afternoon just looking at the table structure. The naming conventions are inconsistent, and there are almost no foreign key constraints—all relationships are managed “in-application” by the PHP logic. But by looking at the tables, I can see the core entities of the business: users, orders, products, and that one weird table called tbl_tmp_report_data_2011 that everyone is probably too scared to delete.

The Big Realisation: It's a Ship of Theseus

After a week of this archaeological dig, I've realised something crucial. This application wasn't designed this way. It grew this way.

It's a digital Ship of Theseus. It was built incrementally, one urgent feature at a time, by smart people working under deadlines. That “messy” file? It was a clean, 200-line script in 2005. The lack of a framework? They didn't exist in a meaningful way when the project started.

The team knows this. They know the brittle parts better than anyone. They've been the ones patching the holes and keeping it sailing for years. My “fresh eyes” aren't seeing anything they don't already know.

But now, I see it, too. I've built the trust of the team (Part 1) and I've built a mental map of the system (Part 2). I finally have the two things I need to ask the most important question: “What's next?”

The business has new features it wants to build. The team wants to modernise. But you can't just stop the world and rewrite a system that's been running for 20 years. That's a recipe for disaster.

You have to do it piece by piece. You have to build the new around the old, slowly and safely.

Next time, we'll talk about the strategy to do just that.

Part 3: Proposing the Strangler Fig Solution.

Cheers,

Jamie C

 
Read more...

from The Pragmatic Pixel

Hey everyone, Jamie here.

Well, here I am again. The new kid in class.

After my last... brief... experiment with the super-corporate world, I knew I needed to get back to an environment where I could feel the impact of my work. I've been fortunate to find a new role, starting this week, at a smaller, established company. It's the polar opposite of where I was a month ago. There's no army of BAs, no abstract time tracking, and thankfully, no meetings about meetings.

Instead, I've walked into something else entirely: a small, deeply-experienced, and very tight-knit development team.

This presents a completely different, and in many ways, more delicate challenge. In a big company, you're just another new hire, a cog sliding into a pre-defined slot. When you join a small, established team, you're the new person at the family dinner. You don't know the in-jokes, you don't understand the history, and you're acutely aware that you're disrupting a dynamic that's been in place for years.

This is the first of a three-part series on this new journey. And before I can even think about the code, I have to navigate the people.


The “New Person” Bubble

The first few days are always a strange experience. You're in a “read-only” state. You're given access to systems, you sit in on conversations, and you nod along, all while your brain is frantically trying to build a map. It's not a technical map, but a social and political one.

  • Who's the “go-to” for infrastructure questions?
  • Who knows the business logic inside-out?
  • What's the story behind “that one server everyone's scared to touch”?
  • What's the real, unwritten process for a deployment?

In a tight-knit team, this map is even more important. These folks have been in the trenches together. They've built, launched, and fixed things as a unit. My presence is an unknown variable. Am I here to “take over”? Am I the “hotshot” who's going to criticise everything? Or am I here to help?

The First Mandate: Listen. (And Then Listen Some More.)

My strategy for the first couple of weeks is simple: talk less, listen more.

I've been in this game long enough to know that I have opinions. I have my preferred stack (AlmaLinux and Caddy, as you know). I have my preferred way of writing code. But on Day One, none of that matters.

Coming in “guns blazing” with suggestions is the fastest way to alienate a team. It's arrogant, and it dismisses the years of context and hard-won lessons that are sitting in their heads.

The single most valuable thing I can do right now is learn. Why are things the way they are?

  • That “weird” deployment script? It was probably written in a hurry at 3 AM to fix a critical outage five years ago, and it's been working ever since.
  • That “outdated” library? They know about it, but it's deeply tied to a core-business function, and the risk of upgrading has never outweighed the benefit.
  • That “missing” test coverage? They're probably more annoyed about it than I am, but they've been fighting other, more urgent fires.

My job is to be a detective, not a critic. I'm gathering context, not ammunition.

Earning Trust > Proving Skill

In a big corp, you prove your worth by closing tickets. In a small team, you prove your worth by being reliable.

My immediate goal isn't to show them how smart I am. It's to show them that I'm a safe pair of hands and that I respect their work.

How do you do that?

  1. Take the Smallest, Most Annoying Bug: I've asked for the lowest-priority, most annoying bug on the board. The one that's been sitting there for months. It's low-risk, it shows willing, and it lets me get my hands dirty without breaking anything important.
  2. Ask Questions, Not “Why?”: The way you ask questions is everything.
    • Bad: “Why on earth are you still using PHP 5.6 for this?”
    • Good: “I'm setting up my local environment for this bug. I see this project is on old PHP. Is there any specific configuration or extension I should be aware of?”
  3. Follow Their Style: My first pull request will be my best attempt at mimicking their existing code style, right down to the variable names and comment blocks. Consistency is more important than my personal preference right now.

This isn't about being passive; it's about being strategic. It's about building social capital. You can't propose any meaningful change until the team trusts that you've understood their current reality.

The Next Step: The Code

After a week of this, I'm starting to feel the edges of the “new person” bubble soften. I've had some good chats, fixed a couple of tiny things, and I'm beginning to understand the team's rhythm. They're smart, dedicated, and have kept a complex system running for a long time.

But now I have to really get to know their life's work.

And that's the next challenge. Because after getting to know the team, my next step is to get to know the code. And this codebase has history. Decades of it.

Next time, I'll be diving into the technical archaeology of Part 2: Understanding Decades-Old Code. and Part 3: Proposing the Strangler Fig Solution.

Cheers, Jamie C

 
Read more...

from The Pragmatic Pixel

Hey everyone, Jamie here.

After my brief and... educational... experience in a heavily abstracted corporate environment, I've been spending a lot of time thinking about the fundamentals. I've missed the feeling of knowing my stack, from the kernel all the way up to the application logic. When you're dealing with endless meetings, opaque processes, and time tracking, you start to crave simplicity, control, and raw performance.

For me, that means getting back to the metal (or as close as is practical).

Running my application hosting, has given me some strong opinions on what makes a stable, efficient, and joyful stack to work with. When I'm not forced to use a pre-defined corporate setup, I have a go-to stack for my Laravel applications. It's lean, it's modern, and it's incredibly robust.

Let's talk about my preferred setup: AlmaLinux, Caddy, and a side of Proxmox.


The Foundation: AlmaLinux

For years, the default “VPS” operating system for most web developers has been Ubuntu. It's a fantastic, user-friendly distro, and I've used it on countless projects.

But when it comes to a production server that I want to set up and then forget about for five years, I lean on the Red Hat (RHEL) ecosystem. With CentOS Stream becoming a rolling release, AlmaLinux has stepped in perfectly as the 1:1 binary compatible, free RHEL fork.

Why AlmaLinux over Ubuntu?

  • Rock-Solid Stability: It's built on the RHEL foundation, which is designed for enterprise-grade stability and long-term support (LTS). The release and update cycle is predictable and thoroughly vetted.
  • Security-First Mindset: Features like SELinux (Security-Enhanced Linux) are integrated from the ground up. While it has a steep learning curve and is the first thing many devs (myself included, on a bad day) turn off, it provides a level of granular security that's hard to beat.
  • No-Nonsense: It feels like a pure, professional server OS. It doesn't come with a lot of the cruft or changing defaults that can sometimes happen in other communities. It's just a stable, secure, and predictable foundation for running services.

The Modern Web Server: Caddy

This is where I get really opinionated. I've spent decades configuring Apache and Nginx. Apache is powerful but feels ancient, and .htaccess files are a source of endless pain. Nginx is a performance marvel, but let's be honest, the configuration syntax is verbose, and setting up SSL with Certbot is an extra step I just don't want to do anymore.

Then I found Caddy.

Caddy is a modern, open-source web server written in Go, and it's an absolute game-changer for a few key reasons:

  • Automatic HTTPS by Default: This is its killer feature. You point a domain at your server, write a 3-line config file, and Caddy automatically provisions and renews Let's Encrypt (and ZeroSSL) certificates for you. No cron jobs, no Certbot scripts, no manual renewal. It just works.
  • The Caddyfile: The configuration file is laughably simple. It's clean, readable, and built for humans.

Want to run a standard Laravel app? Here's a basic Caddyfile for my-laravel-app.com:

my-laravel-app.com {
    # Set the root directory to your app's public folder
    root * /var/www/my-laravel-app/public

    # Enable compression
    encode zstd gzip

    # Handle the "front controller" pattern
    file_server
    try_files {path} {path}/ /index.php?{query}

    # Proxy PHP requests to PHP-FPM
    php_fastcgi unix//run/php-fpm/www.sock
}

That's it. That's the entire file. It handles the HTTPS, the front-controller pattern, and the PHP-FPM connection in 12 clean lines. It makes Nginx config look like assembly language. It's fast, secure by default, and removes so much cognitive overhead.

The Host: VPS or Proxmox?

So, where do I run this AlmaLinux + Caddy stack? It comes down to two choices, depending on the scale.

1. The Rented VPS (The Pragmatic Default)

For most individual projects, this is the way to go. I'll spin up a VPS from a provider like Hetzner, Vultr, or DigitalOcean. Within minutes, I have a root shell on a clean AlmaLinux install. I can install Caddy, PHP-FPM, and my database, deploy my app, and be live in under an hour.

It's the perfect balance of cost, control, and low maintenance. You're not managing hardware, but you have full control over the OS and software.

2. The Proxmox Power-Play

This is where my inner infrastructure nerd comes out. For larger setups or when I want to run multiple isolated services, I turn to Proxmox.

Proxmox is an open-source virtualization platform. You rent one big, powerful bare-metal server and install Proxmox on it. From there, you can create and manage your own Virtual Machines (VMs) and, more importantly, lightweight Linux Containers (LXC).

This gives you a private cloud. I can have:

  • A full VM running AlmaLinux for my primary database server.
  • A lightweight LXC (using minimal resources) for each Laravel app, each with its own Caddy + PHP-FPM install.
  • A separate LXC just for running my queue workers.
  • Another LXC for a Redis server.

This is the ultimate in control and efficiency. You're isolating every part of your application, which is great for security and resource management, all on a single piece of rented hardware. It's more complex, but it's how you build a truly resilient, multi-tenant setup—which is exactly what I do for my hosting clients.


Conclusion: The Joy of a Curated Stack

This stack isn't a “PaaS” (Platform-as-a-Service) like Vapor or Heroku. It's not a one-click deploy. It requires you to know your way around a Linux shell and understand how the pieces fit together.

But after my last role, I'm reminded that this is what I love. I don't want the “black box.” I want a setup where I've chosen every component for a specific reason. This stack—AlmaLinux for stability, Caddy for modern simplicity, and Proxmox for ultimate control—is performant, secure, and, most importantly, a genuine pleasure to work with.

What's your go-to “pragmatic” infrastructure stack? Let me know in the comments.

Cheers,

Jamie

 
Read more...

from Zachary Claret-Scott

#camping #offgrid #tech #solar #anker #outdoors

I love camping. There’s a unique peace that comes from trading city noise for the sound of wind in the trees, especially here in the UK. The goal is always to disconnect, to leave the endless notifications and digital clutter behind for a few days.

And yet, I am a complete hypocrite.

Because as much as I love the idea of being off-grid, the reality is I love having my phone charged. It’s my map, my weather station, my camera for capturing that perfect sunrise, and my podcast player for when it inevitably rains. For years, I played the game of “power anxiety”—draining small power banks, rationing my battery, and telling myself it was part of the authentic experience. It wasn’t. It was just annoying.

I needed a real, sustainable solution that could last a whole trip without me having to think about it. My list of requirements was simple: it had to power my phone, my camera batteries, and maybe a laptop for multiple days, and it needed a way to recharge itself from the sun.

After a lot of research, I found my answer: the Anker SOLIX C300X DC power station and a 100W portable solar panel.

This setup has completely changed my camping experience. The Anker unit itself holds a substantial charge (288Wh), which is more than enough to get me through a long weekend of charging phones, headphones, and camera gear. It has standard USB-A, high-speed USB-C, and 12V DC ports, so it handles everything I can throw at it.

But the real magic is the solar panel. On a reasonably sunny day, the 100W panel can fully recharge the power station, creating a virtually endless loop of free, clean energy. I can use my devices without guilt or anxiety, knowing the battery will be topped up by the time I'm back at camp in the evening.

So, am I still a hypocrite? Probably. I’m hauling a sophisticated piece of tech into the wilderness to escape technology. But this solution doesn't distract from the experience; it enhances it. It gives me the freedom to navigate, photograph, and relax, knowing the practicalities are taken care of. It’s the perfect, pragmatic compromise between being off-grid and being prepared.

 
Read more...

from Dev log //HCAZ

#gitea #git #selfhosted #devops #coding #php

For years, my code has lived on GitHub. It’s the industry standard for a reason: it’s robust, collaboration is seamless, and it’s the heart of the open-source community. For my public and professional work, it's indispensable. However, for my dozens of private projects, experiments, and snippets, I've always felt a slight disconnect. I was relying on a third-party service for code that is deeply personal or in its very early, chaotic stages.

The main issue wasn't trust, but sovereignty and simplicity. I wanted a space that was entirely my own, without the public-facing pressure or the feature bloat of a massive platform. I considered self-hosting a full GitLab instance, but much like my experience with Mastodon, it felt too resource-heavy for my needs. I just wanted a fast, private, and reliable Git server.

That’s when I turned to Gitea.

Gitea is a lightweight, open-source Git server that's incredibly easy to set up. It's a community fork of Gogs and is written in Go, which means it runs as a single binary and has remarkably low resource requirements. I was able to get it running in a Docker container on my own server in under 30 minutes.

The power of having a personal Gitea instance is threefold:

  • Total Privacy: All my code, commit history, and issues are on my own hardware. There's no question about data ownership or who might be scanning my repositories.

  • Blazing Speed: Pushing and pulling from a server on my local network (or a close-by cloud server) is noticeably faster than going through a major commercial service.

  • Simplicity & Control: The interface is clean and familiar, providing all the core features you’d expect (repos, pull requests, issue tracking, wikis) without the overwhelming complexity of larger platforms. It's everything you need and nothing you don't.

Setting up Gitea has given me the perfect private workspace. It complements my public work on GitHub while providing a secure, fast, and fully-owned home for all my personal development. For any developer who values data ownership and a streamlined workflow, it’s a self-hosting project I highly recommend.

 
Read more...

from Virtus Computing

This tutorial continues from the last one in the NotePad app set Basic4Android: Notepad – Part1 (Designing). In this tutorial i am taking the design and adding code to make the app load the text from files and display it in one of the four slots.

App start up

When the app starts up we are going to load in the design with the buttons and the text box in. We are also going to put things into the menu. All of this start up code goes inside the Activity create sub.

Sub Activity_Create(FirstTime As Boolean)
    'Code to run here
End Sub

Loading the layout

First of all loading the layout, all this is done with one line of code which just loads the file we saved in the first tutorial. We made main.bal in the first tutorial and now we are displaying that.

Activity.LoadLayout("main.bal")

Adding menu items

Adding menu items is very simple, we are going to add three menu items Clear, Save, Exit. These will only appear when the menu button is pressed so it saves on screen space. It is one line of code for each menu item. We are only using text items so the line of code is this:

Activity.AddMenuItem("title", "event")

The title is what you want the menu button to say example “Clear” and the event is where you want to run the code example “clear” which would run the sub “clear_Click”. We are adding the three menu items below:

	Activity.AddMenuItem("Clear", "clear")
	Activity.AddMenuItem("Save", "save")
	Activity.AddMenuItem("Exit", "exit")

Loading the slots

When each slot button is pressed two things will happen.

  • The data for that slot will be loaded and displayed.
  • The strings will be changed so that when saved the data is saved to the right slot. We will do the second one first as it is the simplest to do and requires only a few lines of code. First we need to set up the variable to store which slot is open. To do this we Dim the string in the Globals sub. Inside there under the setting up of the buttons and text box add the two lines. Dim slot As String slot = "0" That line lets the program know that if slot is used that it is a bit of data. Right under that line we are going to set slot to 0. This is because we don’t want bugs when a user clicks save straight away. Now we need to put a bit of code into each button so that the slot string changes. For example the button for slot 1 will be: Sub slot1_Click slot = "1" End Sub This needs to be added to all of the other subs, they should have been generated in the first tutorial, so all you need to do is add the second line to each sub. Remember to change the number so that slot2_Click contains slot = “2″ and so on.

Now we need to load the data for each slot. This is very simple to do we just need to load a text file when each button is clicked. this is done in one line, but we are going to make sure the file is there before opening to stop any errors. So under the line above we need to check for a file like this:

If File.Exists(File.DirInternal,"notepadapp-slot" & slot & ".txt") Then
    	notepadbox.Text = File.ReadString(File.DirInternal, "notepadapp-slot" & slot & ".txt")
	ToastMessageShow("Data loaded for slot " & slot & ".", True)
Else
	notepadbox.Text = "There is no data for slot" & slot
	ToastMessageShow("No data loaded for slot " & slot & ".", True)
End If

That code checks to see if the file notepadapp-slow1.txt is there, if it is then it will load the data into the text box. If not then it will display a message. The slot can still be loaded without there being data, and when it is saved for the first time it will create that file for the next time. That code needs to be put in all the four slot buttons and does not need to be modified.

Saving the slots

saving the slots is very easy, it is again just one line of code to do so. You will need to create a new sub like below with the following code inside. It can be anywhere in the code i added it at the bottom.

Sub save_click
	File.WriteString(File.DirInternal, "notepadapp-slot" & slot & ".txt", notepadbox.Text)
	ToastMessageShow("Data saved for slot " & slot & ".", True)
End Sub

The Clear button

The clear button just resets the files to default and removes all the data from it, it is only a few lines of code. it sets the text box to having no data and then writes that to the file. A new sub is need for the clear button just like with save.

Sub clear_click
	notepadbox.Text = "There is no data for slot" & slot
	File.WriteString(File.DirInternal, "notepadapp-slot" & slot & ".txt", notepadbox.Text)
	ToastMessageShow("Data deleted for slot " & slot & ".", True)
End Sub

The exit button

The last button in the menu is the exit button, this is to fully stop the program and not leave it running in the background. I like this in my apps so i can close them and speed up my phone and save battery. The code is very simple and uses a new sub.

Sub exit_Click
	Activity.Finish
	ExitApplication
End Sub

Finishing off

The app now fully works, there is one last thing i want to do to make it work better across phones with big screens. There are many things that can be edited along with colors designs, and loading the slots in a list box. This is the trick i used to make it better on bigger phones it just makes the text box adjust to the screen size, this code goes right after the menu code near the top.

notepadbox.Width = Activity.Width
notepadbox.Height = (Activity.Height - 50)

Done

That is now the app working fully. There is many things that can be added and made better, but this is a working android program that uses files to store data.

Screenshots

notepadappscreenshot (1) notepadappscreenshot (2) notepadappscreenshot (3)

End result

The app on Google Play

Written by Zachary.

 
Read more...

from Virtus Computing

In this set of tutorials i will go through how to make a notepad app. A very simple one with 4 slots and a text box to edit the text in them slots. It will save the data on the SD card if there is one and on the phone if not. I will have settings to change fonts and colours for each slot. This tutorial will be split into 2 parts:

  • Designing
  • Saving and loading slots

After that i will go on to add more functions and explain more things that can be done in B4A and how to use them.

Application settings.

First of all open up B4A and click save, this is because you can not use the GUI designer without saving it first. I recamend making a new folder to save it in like NotepadApp. Put the file name as notepad and click save. Saving the Notepad app.

When it is saved we can start to set up the settings of the app, Like the Name, Icon and Version number. Got to Project and select Choose Icon. Then select the icon from your computer. I am going to use a simple one i found on the internet. (You can download it here)

The next thing we want to edit is Package Name, this is a unique field that identifies your app and will be different from all other apps. Most of the time Developers put this to:

*appname*.*developername*.*companyname*

That is because it has to conation at least two words with a dot between them, it must all be lowercase. In this tutorial i am going to set mine to notepad.virtuscomputing.tutorial. You may use that name as well.

The last one we are going to change today is the Application Label, This is the name of your app and the one that is displayed on the app draw, in your settings, and on the top of the app. I set mine to NotePad.

Designing.

Now we can open up the designer and start to build the GUI. It will be one big Text-box with a tab host along the top. There will be a menu with a Save, Clear and Exit buttons on it. This will be most of the app interface for now. Open up the designer and click Add View then Edit Text, Drag it around the form with the square in the corners. It needs to fit the entire screen, starting from 50 from the top going all the way down to the bottom. You can then add 4 buttons and spread them across the gap left in the top. To make our code easy to read and understand rename the buttons to slot1, slot2, slot3, slot4. And the textbox to notepadbox. This is done by selecting the item, and looking at the top of the list of propities and changing the Name field.

Notepad Design Form

You should end up with something like the form above. At this point you can move thigs around to how you want them to be. Then click File –> Save, and name it main. Once this is done there is one more thing we need to do before starting to code the app. Select Tools –> Generate Members. When the new form opens up Select all views to select all the boxes. On slot1, slot2, slot3, and slot4 open then with the little + to one side and select the box for Click. Once this is done for all of them press Generate Members.

Notepad Generating Members

This inserts all the code into the file to make all them forms appear That is the last of the designing for now. You can move things around in the designer and make them look how you want.

All the code is in Basic4Android: Notepad – Part2 (Code)

Written by Zachary.

 
Read more...

from Virtus Computing

To make the test application we are just going to do the hello world app. This just prints the words hello world on the form to prove that it works. When you clickt he text hello world it will change, just to show how events are handled.

To start when you open Basic4Android it gives you a set of code already. We will be using this code to click Save and select where you want to save the project and all its files. I recommend making a new folder just for the project. Then name the project what you want. here is it called Testing for Tutorial then click save.

After we have save the project the file is created with a .b4a on the end (as seen in the picture above). Once we have saved we can now open up the designer, Click Designer on the menu bar. Two new windows will open up like the below. One of them (The bigger one) Is the control properties window. The other is the Designer.

You can now click Add New and then Label. It inserts the label onto the form and called it Label1 You can drag this around and re size it if you want to (When the item is clicked on the properties window you can change the test font and size. You can input the text there, but we are going to do it programmatic for now. Right click on the label and hover over Generate then Dim Label1 As Label, Then do that again but select Click this time.

Once that has been done we can save the layout, Click File then Save and give the layout a name of main and click Ok. The window should look like the one below.

Once we have done that we can close the designer and begin on the code. There is not allot of code for this program. The first bit it to load the label one we start the program. And then to print text into the label.

Sub Activity_Create(FirstTime AsBoolean)
Activity.LoadLayout("main")
'The line above is to load in the layout we made, remember the name "main"
Label1.Text = "Hello World"
'The line above print the text "Hello World" onto label1
End Sub

The code above should replace the code already in the Activity_Create. The code below is to change the text when the user clicks on the label.

Sub Label1_Click
Label1.Text = "I've been clicked"
'The line above print the text "I've been clicked" onto label1
End Sub

The code above should replace whats in Label1_Click. When the activity is started it calls the first bit of code which sets up the windows and loads in the forms. It then prints the first bit of text to the label. When the label is clicked it calls the second bit of code which changes the text. Make sure to save at this point!

Testing on a android device

If you are using a physical android device then connect it to the same WiFi to the computer(this is the way i do it) Download the Program to your phone (The Basic4Android Bridge linked in the top) Then run it, it needs to configuration and should have some buttons on there. Click the button that says Start – Wireless, it should then start the service. It will say “waiting for connections” when it is ready. Then go to your computer and in basic for android go to Tools then B4A Bridge then Connect – Wireless.

It will prompt you for an IP, This is displayed on your phone and should be something like 192.168.1.68. Type it in and click connect.

If it has worked your phone will say connected.

If it has worked then you can now click the Blue Arrow on basic for android and it will compile the application. When it is done your device will prompt you to install the app automatically. But the APK file is in /objects/Testing for Tutorial.apk so you can send it to your device another way.

Installing the app on the phone. You need to click Install at the first screen, It will then install the application

After it has installed you can Open or Done, You want to open it and make sure it has worked. If you click the label the text will change.

Make sure to press Stop on B4A Bridge to save your battery when you are done.

Written by Zachary.

 
Read more...

from Virtus Computing

Basic4Android is a very useful program to write applications for the android operating system. It runs on windows and has a very similar feel to VB.net. The IDE is very clean and easy to use. And with the remote bridge software you can install the applications straight to a mobile device.

Getting all the files.

Java JDK 7 (32-bit or 64-bit)

http://www.oracle.com/technetwork/java/javase/downloads/jdk7u9-downloads-1859576.html

Android SDK

http://dl.google.com/android/installer_r20.0.3-windows.exe

Basic4Android trail setup (If you have bought it then use the setup sent in the email)

Enterprise Standard (Trial)

Basic4Android Bridge (From the market onto your phone – Optional)

https://play.google.com/store/apps/details?id=anywheresoftware.b4a.b4abridge (http://vcurl.co/?i=f2ea)

Installing

Java

Installing java first as everything needs it. To install java i find it is best to uninstall any Java things you have previously installed. That way you are starting from scratch. Once it has been uninstalled run the Java setup program and install Java to the default place. (The Java JDK development tools will still work for any other java resource. including your browser) Once the Java install has completed do not restart your computer.

Android SDK

Installing the android SDK is a little more complicated. Run the setup and follow through the instructions. Install it all to the default place after the install has completed go to your Start menu and find the “SDK Manager” run that and wait while it starts up (This can take some time). Once it has started up wait for it to refresh the package lists. One it has done select. Android 4.1 (And all that’s inside it) Then click Install packages… You will have to select Agree and Install. Then the bar at the bottom will show the progress of the installation.

Basic4Android

One everything else is installed, i recommend you have restarted before installing Basic4Android. If you are installing the trail then you need to run the file you downloaded at the top. If you have bought it then download the file from your emails and download that file to install. When running the file just install the the default places.

Configuration

When all programs are installed, it is time to configure Basic4Android to work with the Android SDK, and Java JRE. Doing this is very simple and it is only inputting two file paths. One for the javaac.exe and the other for android.jar. To do this open up Basic4Android, Once it has loaded (you will have to select Trail, unless you have a key in which case select the key). Go to Tools then Configure Paths

After you click that you are presented with the window below. You need to locate the two files it says about. It has a location of where they should be but i found that it is not there anymore. Click browse to find the files. Which for me are in these locations (Just remember to change to your username, and correct versions.)

C:\Program Files\Java\jdk1.7.0_07\bin\javac.exe

C:\Users\Zachary Claret-Scott\AppData\Local\Android\android-sdk\platforms\android-16\android.jar

The Additional Libraries folder is if you want to specify an extra folder for it to look for them. It already looks in the program files for them and that’s were we will place them. I will cover libraries in other posts but they allow you to do more stuff like tracking the usages of an app, or talk to ftp.

In the next Guide i will Talk about how to make a application. (Here)

Written by Zachary.

 
Read more...

from keleven

Hopefully this blog will chart my motorbike rides on my trusty Royal Enfield 350 Classic.

I might add other things that I find interesting, then I might not.

 
Read more...

from Zachary Claret-Scott

#music #spotify #lastfm #data

I’ve been tracking my music listening habits for decades on last.fm ever since my dad introduced me to the platform – it’s great to see what music I listened to overtime.

I used to use last.fm daily to find new music by exploring radios, in a time before streaming when ripping mp3s of disk (or online) was king it was the best way to find new music. Once Spotify took over I dumped my local music collection and exclusively listened to streamed music, I had my last.fm connected and mostly ignored it while it silently collected my data.

I’m not sure when last.fm changed but it’s a far throw from its glory days now and it’s hard to see really simple stats about my listening history without signing up for a pro membership. Recently I found an alternative which is exclusive to Spotify called YourSpotify. It is self hosted and pulls recent listening data in real time from the Spotify API but requires a data export from Spotify (which takes up to 30 days) for full historic listening data.

Cover Image

 
Read more...