# Viktor Petersson — Full Content > Insights on DevSecOps, cloud architecture, and building secure, scalable systems. Sharing practical experience from running tech companies and implementing software supply chain security at scale. This file contains the full text of https://vpetersson.com/ in Markdown, intended for consumption by large language models. A shorter index is available at https://vpetersson.com/llms.txt --- # Reading Log URL: https://vpetersson.com/reading-log/ This is mostly a note-to-self kind of page, but perhaps it is useful for someone else too `¯\_(ツ)_/¯`. Here's a partial list (i.e. what I could find in my Amazon history) # 2017 - By Way of Deception by Victor Ostrovsky - Life 3.0 by Max Tegmark - Deep Work by Cal Newport - Industries of the Future by Alec Ross # 2018 - Lost and Founder by Rand Fishkin - Influence by Robert Cialdini - Man's Search For Meaning by Viktor Frankl - Measure What Matters by John Doerr - Zero Trust Networks by Evan Gilman - Ghost In The Wires by Kevin Mitnick # 2019 - How To Be Right... in a World Gone Wrong by James O'Brien - The Master and Margarita by Mikhail Bulgakov - Zen Mind, Beginners Mind by Shunryū Suzuk - I, Robot by Isaac Asimov - Magic Box Paradigm by Ezra Roizen - Digital Minimalism by Cal Newport - The Power of Habit by Charles Duhigg - Why We Sleep by Matthew Walker - The Year Without Pants by Scott Berkun - Scale by Geoffrey West - The Paradox of Choice by Barry Schwartz - Cult of The Dead Cow by Joseph Menn - Prisoners of Geography by Tim Marshall - Principles by Ray Dalio - Animal Farm by George Orwell - The Fish That Ate The Whale by Rich Cohen - Fictions by Jorge Luis Borges - Super Brain by Deepak Chopra and Rudolph E. Tanzi - Zen And The Art Of Motorcycle Maintenance by Robert M Parsig # 2020 - Algorithms To Live By by Brian Christian and Tom Griffiths - Peak by Anders Ericsson and Robert Pool - The Value of Everything by Mariana Mazzucato - Grit by Angela Duckworth --- # Leadership Coaching for Founders: Why Knowing Better Is Not Enough with Ed Ley URL: https://vpetersson.com/podcast/s03e05/ Date: 2026-07-29 Description: Most leadership advice assumes the problem is information, that a founder stuck in the same pattern has simply not been told the right thing yet. The more interesting question is why capable people who already know what they should be doing carry on doing something else. I'm joined by Ed Ley, a leadership coach, author, former gym owner and two-time TEDx speaker, to explore why burnout is rarely a workload problem, what happens when a leader becomes the source of certainty for everyone around them, and how small changes to identity and operating rhythm produce a very different leadership system. Most leadership advice assumes the problem is information, that a founder stuck in the same pattern has simply not been told the right thing yet. The more interesting question is why capable people who already know what they should be doing carry on doing something else. I'm joined by Ed Ley, a leadership coach, author, former gym owner and two-time TEDx speaker, to explore why burnout is rarely a workload problem, what happens when a leader becomes the source of certainty for everyone around them, and how small changes to identity and operating rhythm produce a very different leadership system. Ed's route into this work did not start in a boardroom. It started in fitness, where results are hard to hide and the gap between intention and reality shows up quickly. One of his early answers came from a doctor's surgery, where a doctor handed him the twenty percent of patients taking up eighty percent of his time. They had been told for years to move more and eat better, and the advice had never landed. Ed did not tell them again. He asked them to photograph what they ate, and that one act exposed the distance between the story people held about their habits and what was actually happening. Later, working in Bristol around both high-performance sport and business, he found a useful comparison: athletes and business leaders both commit heavily, but athletes respect recovery and timing in a way business leaders often do not. The idea underneath his work is that most people do not need another generic plan. They need to understand the recipe they are already running. Every outcome has a process behind it, even when that process is unconscious, so the first job is not to prescribe a better routine but to map the existing one clearly enough that someone can see what is producing the current result. That is where the approach parts company with ordinary goal-setting. Rather than treating the brain as a rational machine that needs a destination and a few next steps, Ed treats it as a survival machine reacting to threat, prediction gaps and uncertainty. If it believes a step is dangerous, socially risky or likely to fail, it resists, even when the logic of the plan is sound. The heart of the conversation is burnout, and the distinction Ed draws is worth holding onto. Burnout is not always caused by working too many hours. In leadership it often comes from being the person everyone depends on for certainty. A founder usually has the clearest view of the vision, the product direction and the tradeoffs, and at first that clarity is an advantage. Over time it becomes structural, because everyone starts routing decisions through the founder. Ed describes it as a bungee cord effect, with the leader tied to too many people who keep bouncing back for input or reassurance. The standard advice is to delegate more and rest more, which may be true but is rarely the first useful intervention. Nobody micromanages for fun. Leaders hold on because they are making predictions: that delegating means it comes back broken, that stepping away leaves the team without direction, that not answering stalls something important. So Ed looks at the prediction underneath the behavior rather than the behavior itself. Temporal discounting does the rest, and a leader normalizes a bad operating model because the immediate cost of changing it feels higher than the future cost of keeping it. The same logic shows up in habits: committing to a morning workout is a weak fix when the real blocker is a laptop open until eleven at night and a day with no protected focus window. His example of a CEO who moved from three and a half hours of sleep a night with chronic pain to a steady seven and a half shows how much shifts when the operating pattern is addressed rather than patched with advice. What makes this difficult is that the problem is neither purely personal nor purely organizational. Founders often build the system that later traps them, because the clarity, grit and tolerance for ambiguity that made the company work are the same traits that make everyone else dependent on them. For anyone building scalable systems the lesson is familiar: bottlenecks are rarely solved by asking the bottleneck to work harder. You change the architecture. That means distributing clarity, setting decision boundaries, designing recovery into the operating cadence, and noticing when your identity has become attached to being the person who always has the answer. For founders and engineering leaders, this episode reframes coaching as system design rather than self-improvement. The work is not becoming a different kind of person. It is understanding the system you are already running, then changing the smallest practical pieces that let you lead with more clarity, more trust and less friction. ## Links --- # Post-Quantum Cryptography: Rethinking Trust Before the Clock Runs Out with David Pollak URL: https://vpetersson.com/podcast/s03e04/ Date: 2026-05-27 Description: The encryption underpinning modern civilization was never designed to last forever. It was designed to be computationally hard to break with the machines we had. Google's research now points to 2029 as the year elliptic curve encryption becomes breakable using quantum computers, and the implications stretch far beyond HTTPS. I'm joined by David Pollak, known widely as dpp, a technologist with nearly five decades of experience and the founder of Spice Labs, to explore what post-quantum cryptography actually means, why the transition is harder than almost anyone is treating it, and what organizations need to start doing now before the window closes. The encryption underpinning modern civilization was never designed to last forever. It was designed to be computationally hard to break with the machines we had. Google's research now points to 2029 as the year elliptic curve encryption becomes breakable using quantum computers, and the implications stretch far beyond HTTPS. I'm joined by David Pollak, known widely as dpp, a technologist with nearly five decades of experience and the founder of Spice Labs, to explore what post-quantum cryptography actually means, why the transition is harder than almost anyone is treating it, and what organizations need to start doing now before the window closes. David's career resists easy summary. He wrote nuclear bomb damage estimation systems for FEMA at the age of fourteen, built spreadsheet software for NeXT computers that beat Lotus out of the market with a three-person bootstrapped company, and spent years consulting at Twitter in 2008 when the site was failing under its own growth. His work there involved diagnosing the root cause of the fail whale, a subtle interaction between Ruby's garbage collector and co-located memcached processes, and ultimately rewriting Twitter's public timeline endpoint to reduce fifteen boxes of infrastructure down to one. From there he moved to Cisco working on enterprise firewalls, and the thread running through all of it was a recurring encounter with systems where security assumptions had quietly broken down at the infrastructure level, long before anyone noticed. That thread eventually led to Spice Labs. The insight behind the company came from applying the same Merkle tree logic that Git uses for source code to post-build artifacts instead, creating a graph of hash values across open source and proprietary software to map what cryptography is actually being used and where. The distinction matters because most existing tools for cryptographic discovery rely on source code access or build manifests like Maven and Gradle files, which do not reflect what was actually compiled into a binary. Teams frequently copy cryptographic libraries directly into applications without declaring them anywhere. By working from the artifact itself rather than the recipe, Spice Labs can find what is actually running, including the dependencies nobody documented and the legacy code nobody owns anymore. We go deep on why post-quantum cryptography is both an urgent and a genuinely difficult problem to solve. David walks through the mechanics of Shor's algorithm, which uses quantum superposition to perform prime factorization at speeds that make current RSA and elliptic curve encryption trivially breakable at sufficient qubit counts. The engineering constraints around quantum coherence have kept the threat theoretical, but Google's 2029 timeline and China's demonstrated ability to break 50-bit RSA suggest the gap is closing faster than most people assumed. The replacement candidates, a family of lattice-based algorithms standardized by NIST, carry real tradeoffs: larger key sizes, heavier computation, and no hardware acceleration in current processors, which means every system running cryptographic operations today is looking at a performance regression when it migrates. David introduces the concept of crypto agility as the key architectural principle, defining algorithm choices in configuration rather than baking them into source code, so that future algorithm updates do not require deep rewrites across an entire stack. What makes the transition genuinely hard is not the cryptography itself but the inventory. David draws a sharp contrast between the Y2K problem, which was bounded by physical mainframe leases and gave CIOs a finite list of systems to audit, and today's environment of millions of microservices, continuous delivery pipelines, and artifacts spread across cloud infrastructure with no central registry. The discovery problem is compounded by misaligned authority: CISOs and CIOs are responsible for certifying compliance but cannot require every engineering team to install a source-code scanning tool. The more tractable path is inspecting artifacts at the registry level, identifying which of twenty thousand microservices are performing digital signing operations, and narrowing the remediation conversation to a manageable scope. For legacy systems, whether COBOL on a mainframe or Java 5 code built on deprecated Sun classes, the options are proxying behind a quantum-safe layer or scoping a rewrite, and David is direct that AI-assisted migration is only credible where test coverage exists to validate the output. The organizational reality is that hardware roots of trust, including the HSMs and TPMs backing certificate authorities and device signing infrastructure, are almost entirely built on classical PKI and are not quantum-safe. Even AWS, which David describes as taking this more seriously than most, has set an internal deadline of 2028. For engineers and security teams, this episode reframes the post-quantum cryptography transition as a discovery and prioritization problem before it is a remediation problem. The organizations that begin mapping their cryptographic surface area now, identifying where digital signing, at-rest encryption, and key management are actually happening across their software estate, will have the lead time to plan. The ones that treat this as a future problem will find themselves scrambling to remediate under pressure, with a deadline set not by a compliance framework but by the pace of quantum hardware development. ## Links --- # Container Security Runtime: Rethinking Isolation with Alex Zenla URL: https://vpetersson.com/podcast/s03e03/ Date: 2026-05-05 Description: Containers were never designed to be a security boundary, yet we have spent the last decade treating them like one. I'm joined by Alex Zenla, Founder and CTO of Edera, to explore how that assumption shaped modern systems and why it is starting to break under real-world pressure. Containers were never designed to be a security boundary, yet we have spent the last decade treating them like one. I'm joined by Alex Zenla, Founder and CTO of Edera, to explore how that assumption shaped modern systems and why it is starting to break under real-world pressure. From insecure IoT deployments to container-heavy production stacks, Alex brings a perspective grounded in building and breaking systems at scale, and what it actually takes to rethink isolation from first principles. Alex's background starts early, contributing to open source projects and working deeply in the world of IoT, one of the most notoriously insecure domains in tech. Working with industrial systems exposed a harsh reality: outdated software, fragile networking assumptions, and environments where security was an afterthought. These experiences, particularly at Google, led him to question whether the underlying infrastructure we rely on can ever be made secure without fundamentally changing how workloads are isolated. That question ultimately led to Edera. What began as an attempt to secure constrained IoT devices evolved into a broader realization that containers are the wrong abstraction for strong isolation. Alex and his team explored alternatives, moving away from namespace-based models toward hypervisor-driven approaches that create real boundaries between workloads. Along the way, they faced trade-offs around performance, compatibility, and developer experience, especially in ecosystems like Kubernetes where expectations are deeply ingrained. We dig into what it means to build a container security runtime that treats isolation as a first-class concern rather than an afterthought. This means rethinking everything from how virtual machines are provisioned to how file systems, volume mounts, and networking are handled. Alex walks through the nuances of kernel-level risks, the limitations of existing tools like gVisor and Kata, and why seemingly simple features like volume mounts introduce significant attack surfaces. The result is a system that blends virtualization and container workflows in a way that feels familiar to developers but operates with a fundamentally different security model. We also explore the broader industry dynamics that got us here. Developer experience drove the adoption of containers, but often at the cost of security guarantees. Compliance frameworks can reinforce outdated practices instead of encouraging better ones. And as AI agents introduce highly autonomous, non-deterministic workloads, the need for secure, ephemeral, and reversible environments becomes even more urgent. Alex's thinking points toward a shift in how we approach compute itself, moving from static systems to ones that can safely execute, roll back, and adapt in real time. For builders and security-minded engineers, this episode reframes container isolation as something that needs to be redesigned from the ground up, not patched on top of an abstraction that was never built for it. ## Links --- # SBOMs & CRA Compliance with Olle Johansson and Anthony Harrison URL: https://vpetersson.com/podcast/s03e02/ Date: 2026-03-24 Description: What happens when cybersecurity stops being an afterthought and becomes a legal obligation? In this episode of Nerding Out with Viktor, I'm joined by Olle Johansson, CycloneDX member and lead of the Transparency Exchange API, and Anthony Harrison, a UK-based software supply chain security consultant and open source SBOM tooling author, to explore how SBOMs, the Cyber Resilience Act, and the Product Liability Directive are reshaping how secure products are designed, maintained, and brought to market. What happens when cybersecurity stops being an afterthought and becomes a legal obligation? In this episode of Nerding Out with Viktor, I'm joined by Olle Johansson, CycloneDX member and lead of the Transparency Exchange API, and Anthony Harrison, a UK-based software supply chain security consultant and open source SBOM tooling author, to explore how SBOMs, the Cyber Resilience Act, and the Product Liability Directive are reshaping how secure products are designed, maintained, and brought to market. Both guests come from hands-on engineering backgrounds. Olle built open source telephony systems that ended up in critical infrastructure - air traffic control, emergency services, elevators - and now works on TC54 standardization and the Transparency Exchange API within CycloneDX. Anthony moved from building complex systems into focusing on software supply chain risk, running his own consultancy and contributing open source SBOM tools. Together they co-founded SBOM Europe, a community effort to improve software transparency practices across the continent. Incidents like Log4j and SolarWinds were turning points - not just because of the vulnerabilities themselves, but because the industry couldn't quickly answer a basic question: where are we exposed? That gap is what SBOMs are meant to close. We unpack how CRA differs from NIS2 and DORA, and why that distinction matters. CRA targets products placed on the market, while NIS2 is a directive implemented differently across member states. The new Product Liability Directive now includes software for the first time, meaning manufacturers face real consequences if their products cause harm. Liability follows the integrator - if you ship firmware, hardware, or open source components, you own the whole stack. Unlike traditional CE marking, which is a one-time checklist, CRA demands ongoing process compliance: risk analysis, threat modeling, and maintenance for the entire declared product lifetime. The upcoming EU Omnibus proposal aims to simplify this cross-border regulatory patchwork, but for now, compliance is less about submitting a perfect document and more about building processes that demonstrate due diligence. The conversation also turns to what this means for engineering teams day to day. SBOM tooling is inconsistent - the practical advice is to never rely on a single tool, since different tools produce different dependency counts for the same codebase. Anthony discusses the UK's Software Security Code of Practice and its limitation as a voluntary framework without enforcement teeth. Olle raises the looming challenge of post-quantum cryptography, arguing that products need crypto agility - the ability to swap cryptographic engines during a product's lifetime - which most companies have not considered. AI can improve code quality and auditing, but it risks eroding the deep engineering knowledge needed when automated tools fall short. Security can no longer be a late-stage checkbox - it has to be built into product development from the start. One principle anchors the discussion: any SBOM is better than no SBOM. Perfection is not the expectation. Demonstrable effort is. For founders, embedded engineers, and product teams navigating the new regulatory landscape, this episode offers a practical look at what CRA compliance actually demands and where to start. ## Links --- # Biohacking Resilience: Engineering the Human System with Marcelo Garcia URL: https://vpetersson.com/podcast/s03e01/ Date: 2026-02-11 Description: Marcelo Garcia, telecom infrastructure veteran turned extreme biohacker, joins me to open Season 3 of Nerding Out with Viktor. From completing a 50-day water-only fast while hiking nearly 1,000 kilometers across Spain to detailed biometric tracking with DEXA scans, VO2 max testing, and continuous glucose monitoring, Marcelo applies engineering discipline to human physiology, treating the body as a system you can stress test, instrument, and optimize. What happens when you deliberately stress test your own operating system? In this Season 3 opener, I'm joined by Marcelo Garcia, a telecom infrastructure veteran turned extreme biohacker, to explore what it looks like when engineering discipline meets human physiology. Marcelo recently completed a 50-day water-only fast while hiking nearly 1,000 kilometers across Spain, capturing detailed biometric data across DEXA scans, VO2 max testing, HRV tracking, metabolic panels, and continuous glucose monitoring. Marcelo's background in infrastructure shapes how he approaches the body. He treats physiology the way we treat systems: define constraints, introduce load, measure response, iterate. The 50-day fast was not about spectacle. It was about testing fat oxidation limits, understanding muscle preservation under sustained movement, and identifying where real physiological bottlenecks appear. One key insight is that movement during fasting changes the signal entirely. If the body is not eating but still moving, it adapts differently than if it's simply at rest. We dig into the mechanics behind prolonged fasting, from ketosis and gluconeogenesis to autophagy and immune regeneration. Beyond the metabolic layer, we explore biological age modeling and how tools like DEXA scans, metabolic carts, and wearable data can now be combined with AI to synthesize over a hundred biomarkers into healthspan projections. The recurring theme throughout is feedback loops. You cannot optimize what you do not instrument. At its core, this conversation is about endogenous biohacking: not adding more tools, but reactivating built-in systems through controlled stress. For builders and technically inclined listeners, this episode reframes resilience as something you design, measure, and refine, just like any other system you care about. ## Links --- # CRA Explained: What the Cyber Resilience Act Means for Device Manufacturers with Sarah Fluchs URL: https://vpetersson.com/podcast/s02e21/ Date: 2025-12-17 Description: What happens when long-lifecycle hardware meets modern cybersecurity expectations? In this episode of Nerding Out with Viktor, we're joined by Sarah Fluchs, CTO and OT cybersecurity expert, to break down how the Cyber Resilience Act (CRA) is shifting the way secure products are built, maintained, and supported across the EU market. With first-hand involvement in the EU Commission's CRA expert group, Sarah brings a rare blend of technical and policy perspective to an issue that touches every device manufacturer. What happens when long-lifecycle hardware meets modern cybersecurity expectations? In this episode of Nerding Out with Viktor, I sit down with Sarah Fluchs, CTO and OT cybersecurity expert, to break down how the Cyber Resilience Act (CRA) is shifting the way secure products are built, maintained, and supported across the EU market. Sarah began her career as a mechanical and process engineer before moving into OT security, eventually earning a PhD in security-by-design. Her research intersected directly with the early drafts of the CRA, which led to her appointment on the EU Commission's CRA expert group. That first-hand involvement gives her a rare blend of technical and policy perspective on a piece of legislation that touches every device manufacturer. As the conversation unfolds, we explore the friction between CRA's security goals and the practical realities of embedded systems. From OTA update limitations to certification constraints, Sarah outlines the challenges manufacturers face and how they're adapting. We dig into why "patch it fast" doesn't scale in OT environments, and how CRA's five-year support requirement forces companies to rethink lifecycle planning from the ground up. The discussion also turns to SBOMs: what they are, why they matter, and the very real difficulty of generating them in legacy systems. Along the way, we touch on tooling, data quality, versioning, and the operational mess behind making software components traceable and auditable across complex supply chains. Underlying it all is a simple message: CRA compliance isn't about ticking boxes. Done right, it's a path to stronger engineering practices, deeper supply chain accountability, and better product security. For anyone building secure, connected devices in a regulated world, this episode offers a practical guide to what's coming and how to prepare. ## Links - [EU Cyber Resilience Act](https://digital-strategy.ec.europa.eu/en/policies/cyber-resilience-act) - Official EU page on the Cyber Resilience Act --- # UK Online Safety Act: Digital ID and the Risks of a Database State with James Baker URL: https://vpetersson.com/podcast/s02e20/ Date: 2025-11-19 Description: What happens when a government's push for "safety" starts reshaping the architecture of the internet? In this episode of Nerding Out with Viktor, we're joined by James Baker, Policy and Campaigns Manager at Open Rights Group, to unpack the deeper implications of the UK Online Safety Act and the country's evolving approach to Digital ID. With two decades of work in privacy advocacy, James offers a grounded but no less urgent perspective on how these policies affect builders, platforms, and users alike. What happens when a government's push for "safety" starts reshaping the architecture of the internet? In this episode of Nerding Out with Viktor, I'm joined by James Baker, Policy and Campaigns Manager at [Open Rights Group](https://www.openrightsgroup.org/), to unpack the deeper implications of the UK Online Safety Act and the country's evolving approach to Digital ID. With two decades of work in privacy advocacy, James offers a grounded but no less urgent perspective on how these policies affect builders, platforms, and users alike. What begins as a conversation about compliance quickly leads into bigger questions about identity, control, and the future of privacy in digital systems. James began his work in digital rights in the early 2000s with the NO2ID campaign, helping defeat the UK's first national ID card proposal. Since then, he's worked across local government, EU institutions, and privacy advocacy, now leading policy at Open Rights Group — often described as the UK's EFF. That long view shapes his critique of today's Digital ID and surveillance proposals, where control over identity is quietly shifting away from individuals. As James explains, many concerns around Digital ID in the UK aren't rooted in the technology itself but in how it's governed. Unlike decentralized or bank-issued identity systems seen in countries like Sweden and Estonia, the UK model leans heavily toward centralization. That raises alarms about a so-called "Database State," where access to public services could be controlled, revoked, or surveilled at scale. Combine that with AI-driven policy decisions and the involvement of players like Palantir, and you have a potent recipe for institutional overreach without the constitutional safeguards seen in other democracies. Throughout the episode, we dive into the technical consequences of these policy shifts. From mandated age verification to perceptual hash matching and metadata scanning, the requirements of the Online Safety Act place growing burdens on infrastructure teams. As James puts it, the UK's enforcement model risks fragmenting the open internet, pushing developers toward compliance-heavy centralization or forcing them to exit the UK market altogether. Even tools like end-to-end encryption and VPNs are under quiet pressure, with the line between user protection and mass surveillance getting harder to draw. What emerges isn't just a policy critique. It's a design problem. James outlines what a privacy-first digital ID could look like: built on W3C standards, backed by legal protections, open source, and decentralizable by design. It's a vision that trusts the user instead of defaulting to control. And while he's cautious about political will, he's optimistic that builders can still shape a better way forward, especially when armed with the right tools and a clear understanding of what's at stake. If you care about open source, encryption, or building systems that scale responsibly, this is an episode you'll want to catch. ## Links - [UK Online Safety Act 2023](https://www.legislation.gov.uk/ukpga/2023/50/contents) - The full text of the Online Safety Act - [Open Rights Group](https://www.openrightsgroup.org/) - UK digital rights organization, often described as the UK's EFF - [NO2ID Campaign](https://en.wikipedia.org/wiki/NO2ID) - The campaign that defeated the UK's first national ID card proposal - [W3C Decentralized Identifiers (DIDs)](https://www.w3.org/TR/did-core/) - W3C standard for decentralized digital identity - [W3C Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) - W3C standard for privacy-preserving digital credentials - [EFF Digital Identity Resources](https://www.eff.org/issues/digital-identity) - Electronic Frontier Foundation's work on digital identity --- # Rethinking Software Security Compliance in the Age of AI with Nick Selby URL: https://vpetersson.com/podcast/s02e19/ Date: 2025-11-05 Description: Nick Selby, a veteran security leader known for his clear-eyed approach to building practical, human-centered security programs, joins Viktor to tackle a central question that haunts many growing tech companies: why do so many teams treat software security compliance as a box-ticking exercise, and what does that mindset cost them? Drawing from decades of experience, Nick brings both humor and hard-won lessons to an issue that often feels more bureaucratic than technical. Why do so many teams treat software security compliance as a box-ticking exercise, and what does that mindset cost them? In this episode of Nerding Out with Viktor, I sit down with Nick Selby, a veteran security leader known for his clear-eyed approach to building practical, human-centered security programs. Nick traces his path from the early days of hands-on security work, where risk was measured by intuition and experience, to the era of standardized frameworks and regulatory oversight. He reflects on how compliance frameworks like SOC 2 and ISO 27001 emerged to bring order to chaos but often became ends in themselves. Instead of driving real-world resilience, they sometimes created a false sense of assurance. For Nick, this isn't just a process problem; it's a cultural one. He argues that companies should see compliance as the outcome of good security practices, not the starting point. As the conversation deepens, we explore the messy middle between technical rigor and operational reality. We discuss the rise of AI tools in engineering workflows and how adoption often outpaces security readiness. Nick shares examples of companies racing to integrate generative AI without understanding the privacy and threat implications. We dig into the cultural dynamics that cause even well-intentioned teams to make risky shortcuts, especially when compliance audits and investor expectations start to shape technical decisions more than engineering principles. The discussion also turns toward the evolution of modern frameworks and legislation, from the Cyber Resilience Act to the growing importance of SBOMs (Software Bill of Materials) in software supply chains. Nick explains how these measures can bring much-needed transparency to vendor accountability but warns against treating them as magic bullets. Compliance, he suggests, should evolve alongside the products themselves, adapting as systems grow, architectures shift, and user expectations change. By the end of the episode, the message is clear: software security compliance should serve the business, not the other way around. Nick and I leave listeners with a challenge to rethink how they build trust and resilience into their systems, not just their documentation. For anyone passionate about open source, product development, or building technology that lasts, this is a conversation worth tuning into. ## Links - [Safety Co-Option and Compromised National Security: The Self-Fulfilling Prophecy of Weakened AI Risk Thresholds](https://arxiv.org/abs/2504.15088) - [EU Cyber Resilience Act](https://digital-strategy.ec.europa.eu/en/policies/cyber-resilience-act) --- # The Quiet Power of Digital Minimalism with Patrick Walker URL: https://vpetersson.com/podcast/s02e18/ Date: 2025-10-22 Description: Patrick Walker, former YouTube and Facebook executive turned conscious tech founder, shares his journey from shaping the early internet video landscape to building tools that prioritize intention over engagement. From YouTube's copyright wars to Facebook's ethical friction around live video and algorithms, Patrick reflects on the psychological toll of attention-based products and his pivot to digital wellness through Uptime, a micro-learning app designed to fight passive consumption. Is tech making us smarter—or just more distracted? In this episode of Nerding Out with Viktor, I sit down with Patrick Walker, former YouTube and Facebook executive turned conscious tech founder, to explore how we got here and what it takes to break free from the default. Patrick's journey spans the early days of digital media, from working as a TV producer in Japan to navigating YouTube's copyright wars and platform expansion across global markets. At Facebook, he witnessed firsthand the ethical friction behind live video, algorithm design, and the psychological toll of attention-based products, especially on younger users. The conversation dives into the pivotal moments that shaped Patrick's perspective on technology's impact. He reflects on his career spent scaling platforms like YouTube globally, helping define the infrastructure that powers modern video consumption, and eventually making the difficult decision to leave Big Tech behind. That turning point led to Uptime, a micro-learning app designed to fight passive consumption and offer a smarter way to engage with content. Patrick unpacks the product thinking behind building for digital wellness, explaining how it requires more than just good UX—it demands restraint, values, and long-term perspective in an industry optimized for the opposite. Whether you're building tech, raising kids around screens, or just trying to regain your focus, this episode offers a practical look at how to design and live with more clarity in an attention-driven world. --- # Coreboot and Linux Hardware: Inside Star Labs with Sean Rhodes URL: https://vpetersson.com/podcast/s02e17/ Date: 2025-09-17 Description: Sean Rhodes, Firmware Engineer at Star Labs, shares the journey of building Linux-first laptops and mini PCs backed by open firmware. From the early days as a side project to surviving supply chain crises and choosing coreboot over AMI BIOS, Sean explains what it takes to create reliable hardware for the open source community. How do you design and ship coreboot and Linux hardware that actually works in the real world? In this episode of Nerding Out with Viktor, I sit down with Sean Rhodes, Firmware Engineer at Star Labs, to explore what it takes to build Linux-first laptops and mini PCs backed by open firmware. Sean reflects on the early days of Star Labs, when a handful of engineers set out to create laptops that ran Linux reliably. What began as a side project grew into a full-fledged company with a reputation for supporting open source systems. Along the way, the team faced high tooling costs, unpredictable supply chains, and the challenge of meeting the needs of a niche but demanding user base. As the conversation unfolds, Sean details how Star Labs survived the COVID-era supply chain crisis, why they moved away from AMI BIOS in favor of coreboot, and how firmware updates delivered through LVFS became central to their long-term reliability strategy. He also explains why some features, like fingerprint readers, failed to deliver real-world value. At its core, the Star Labs philosophy is about empowering users with secure, reliable, and open hardware. From haptic trackpads to distro-agnostic support for Fedora, Qubes, and openSUSE, Sean's perspective makes clear that Linux-first machines can thrive outside the mainstream PC market. --- # Lessons in Building Niche Hardware and Networks with Planet Computers' Marcus Taylor URL: https://vpetersson.com/podcast/s02e16/ Date: 2025-09-03 Description: Marcus Taylor, co-founder of Planet Computers, has lived through multiple waves of computing revolutions - from early natural language systems at IBM in the '90s to pioneering mobile commerce and secure communications well before the smartphone era. In this episode of Nerding Out with Viktor, he shares the lessons learned from a career at the intersection of hardware, networks, and resilient systems. Marcus Taylor's journey into technology spans over three decades, but his latest work at Planet Computers represents a fascinating convergence of everything he's learned about hardware, networks, and resilient systems. As co-founder of Planet Computers, Marcus brings a rare operational lens to building niche devices that serve specific communities. Marcus' story begins with IBM labs and Logica, where he explored natural language processing and early mobile wallet prototypes in the mid-90s. Those experiments laid the groundwork for later projects at Ericsson, Digital Mobility, and the GSM Association, where he navigated the politics of telco standards and helped bring Bluetooth and Wi-Fi into the mainstream. The conversation then turns to Erlang, the programming language behind WhatsApp and critical banking systems. Taylor recounts how his consultancy supported Ericsson, financial institutions, and global telecom providers in building fault-tolerant infrastructure that could handle real-world demands - from base stations to payment clearing systems. From there, we dive into Planet Computers - the company reviving the keyboard-first form factor through devices like the Gemini, Cosmo, and Astro. These Planet Computers smartphones are niche by design, serving journalists, security teams, and technologists who need multi-boot Android/Linux flexibility and secure communications. Marcus explains the challenges of building hardware for specialized markets and the economics of competing with giants like Samsung and Apple. The episode explores how Planet Computers has evolved beyond smartphones into network appliances for Industry 4.0 applications. Marcus details their work with AI-powered CNC machine monitoring in Singapore, using edge computing to give machinists real-time feedback on tool wear and machine performance. We also discuss their logistics tracking system using mesh networks for Brexit-related customs automation. Taylor closes with reflections on the future of telcos, private 5G networks, and preventive health technology through his work with Invisa smart shoes. For builders, engineers, and founders, this is a rare perspective on how niche devices and resilient networks still shape the backbone of modern systems, and why specialized hardware continues to find its place in an increasingly software-defined world. --- # Nerding Out on Software Supply Chain Security with ITSPmagazine's Sean Martin URL: https://vpetersson.com/podcast/s02e15/ Date: 2025-08-18 Description: I'm joined by Sean Martin, co-founder of ITSPmagazine and veteran security expert, to explore the evolving landscape of software supply chain security. Recorded live at Hacker Summer Camp, we dive into how transparency is replacing vague security claims and why SBOMs are becoming real-time operational assets. Sean Martin's journey into software security spans over three decades, but his latest focus, software supply chain security, feels more relevant than ever. As co-founder of ITSPmagazine and a veteran of countless product launches at Symantec, Sean brings a rare operational lens to security. In this crossover episode of Nerding out with Viktor, recorded live at Hacker Summer Camp, Sean joins me to unpack what's changing in the security landscape, from SBOM adoption to the increasing pressure from regulations like the CRA. We explore how supply chain security is becoming table stakes, how transparency is replacing vague claims ("saying you're secure means absolutely nothing"), and why SBOMs are evolving into real-time operational assets rather than static compliance artifacts. The conversation dives deep into practical workflows, from integrating SBOMs in CI/CD to refactoring legacy systems and building AI-powered developer agents. We discuss why automation and guardrails are critical when working with LLMs, and how engineers may soon supervise fleets of purpose-built agents instead of writing every line of code themselves. From fish tank hacks to IoT compliance theatre, the episode is full of examples where security promises break down without real architecture behind them. Sean's three decades of experience shine through as he connects historical patterns to current challenges, showing how the fundamentals of good security architecture remain constant even as the tools and threats evolve. --- # Navigating SBOMs at Scale: Inside DependencyTrack with Niklas Düster URL: https://vpetersson.com/podcast/s02e14/ Date: 2025-07-16 Description: I'm joined by Niklas Düster, co-lead of OWASP Dependency Track, to explore how teams manage software bill of materials (SBOMs) at scale. From solving vulnerability scan overload to becoming a project co-lead alongside Steve Springett, Niklas shares how Dependency Track enables continuous visibility across thousands of components. Niklas Düster got involved with [Dependency Track](https://dependencytrack.org/) the way many great open source stories begin: by trying to solve a problem at work. As a security engineer, he was drowning in vulnerability scan reports that didn't scale. When he discovered Dependency Track and CycloneDX, he not only adopted them but began contributing, eventually becoming the project's co-lead alongside Steve Springett. In this conversation, Niklas walks us through how Dependency Track helps teams stay on top of their software bill of materials (SBOMs), enabling continuous visibility across thousands of components. We dig into real-world workflows, from uploading SBOMs in CI/CD pipelines to responding quickly during incidents like Log4Shell and how version 5 of the platform is evolving to support horizontal scaling, smarter suppression logic, and role-based access control. He also breaks down how teams use VEX files to add context to vulnerabilities, why gating deployments can backfire, and how CEL-based policy conditions offer a more flexible way to manage findings. The goal isn't just to scan for issues, but to track them intelligently across the full software lifecycle. Underlying it all is a clear philosophy: prioritize usability, support real engineering workflows, and keep the system lean enough that newcomers can adopt it without needing a Kafka cluster. Niklas's focus is on making open source tooling that actually works in production, not just in theory. --- # Rethinking Startups: Inside the Venture Studio Model with Thorbjørn Rønje URL: https://vpetersson.com/podcast/s02e13/ Date: 2025-07-02 Description: I'm joined by Thorbjørn Rønje, founder of Bifrost Studios, to discuss why venture studios are the most effective path to building companies. Drawing from nearly 50 startups, he shows how repeatable systems beat lone heroics when launching new products. Thorbjørn started with a simple insight: traditional entrepreneurship is inefficient. Too much learning happens on the fly, wasting time and capital. In this conversation, he explains how the venture studio approach de-risks the early phases by codifying what works, from building teams to finding product-market fit. We dig into Bifrost's "Purple Ocean" framework for selecting ideas, their rapid product validation process, and why the studio hands off each project once it's ready to scale. It's a model designed for speed, clarity, and capital efficiency. Thorbjørn also shares how these playbooks apply to acquisitions, reviving legacy businesses using the same systems and technology. Underpinning it all is a practical philosophy: build what users need, test quickly, and avoid unnecessary complexity. We wrap up by looking ahead at how AI and post-labor economics might reshape company building, ownership, and income distribution. --- # Inside System76: Building Open Hardware and a Linux-First Future URL: https://vpetersson.com/podcast/s02e12/ Date: 2025-06-18 Description: I'm joined by Carl Richell, founder and CEO of System76, for an in-depth look at what it takes to build an open source hardware company from the ground up. Carl's journey, from launching Ubuntu laptops before Linux was mainstream to building open firmware, a desktop environment, and even their own factory, is one of the most hands-on stories in open source computing. In this episode, I'm joined by Carl Richell, founder and CEO of System76, for an in-depth look at what it takes to build an open source hardware company from the ground up. Carl's journey, from launching Ubuntu laptops before Linux was mainstream to building open firmware, a desktop environment, and even their own factory, is one of the most hands-on stories in open source computing. We begin with the early days of System76, where Carl shares what it was like to ship Linux hardware when driver support was shaky and installing an OS meant burning CDs. His focus on delivering a seamless out-of-box experience and supporting users directly helped System76 stand out in a time when Linux on laptops was far from a given. The conversation really picks up as we talk about System76's move into manufacturing. Carl explains how and why they decided to build their own factory, and what they've learned from designing everything from the chassis to the firmware. We get into supply chain complexity, the challenges of BIOS-level access, and the value of having full visibility into what goes into your hardware. On the software side, we talk about the evolution from Ubuntu to POP!_OS, and how feedback from users shaped its development. Carl also shares the thinking behind Cosmic, their new desktop environment written in Rust, which is built to be modular and deeply customizable. His approach to user experience, design flexibility, and upstream contributions reflects a strong balance of engineering discipline and community focus. What really stands out is how System76 blends idealism with pragmatism. Carl talks about what it means to stay open in a world full of proprietary barriers, how they navigate licensing, and why they're committed to making computers more auditable, secure, and user-owned. If you're curious about how a small team can take on hardware, firmware, and software while staying true to open source values, this episode is for you. Carl brings a rare mix of vision, clarity, and hands-on experience that makes this a compelling listen for anyone building in public. --- # The Systems Behind Managing High-Performing Remote Teams with Jon Seager URL: https://vpetersson.com/podcast/s02e11/ Date: 2025-06-04 Description: I explore the systems and practices that make remote teams thrive with Jon Seager from Canonical. We dive into measuring output over activity, calendar ownership as a performance indicator, and how to build trust and autonomy in distributed organizations. In this episode, I'm joined by Jon Seager, VP of Engineering at Canonical, for a deep dive into the systems and practices that make remote teams thrive. Jon's experience scaling Canonical's distributed engineering organization offers unique insights into what really works in remote leadership. We start by exploring how Canonical structures its fully distributed engineering organization. What particularly caught my attention was Jon's approach to measuring output rather than activity, and how this fundamental shift in perspective drives better results. His explanation of calendar ownership as a performance predictor reveals the subtle but crucial aspects of remote work culture. The conversation gets especially interesting when we dive into the practical aspects of remote team management. Jon shares valuable insights about structuring effective one-on-ones and retrospectives, emphasizing the importance of clear communication and documentation. His perspective on setting up ergonomic home offices and maintaining healthy boundaries shows how physical workspace impacts remote productivity. I was particularly intrigued by our discussion of documentation and knowledge sharing. Jon's approach to combating documentation debt and tribal knowledge reveals how Canonical maintains clarity across its distributed teams. We also explore the delicate balance between trust and accountability, with Jon offering practical strategies for building autonomy without micromanagement. If you're leading remote teams or scaling distributed organizations, you'll find plenty of practical insights here. Jon brings both deep experience and thoughtful analysis to the discussion, making complex remote management concepts accessible while maintaining their practical depth. --- # Why Web3, Crypto, and Blockchain Still Struggle in the Real World URL: https://vpetersson.com/podcast/s02e10/ Date: 2025-05-22 Description: In this episode, I talk with Vlad Trifa, founder of Zimt and co-founder of EVRYTHNG, about the challenges of applying Web3, crypto, and blockchain in real-world systems. In this episode, I talk with Vlad Trifa, founder of Zimt and co-founder of EVRYTHNG (bought by Digimarc), about the challenges of applying Web3, crypto, and blockchain in real-world systems. With a background in IoT, supply chain tracking, and digital product identity, Vlad shares practical lessons from working on blockchain projects outside of the lab. We discuss what often goes wrong when these technologies are used in enterprise environments, and where they might still be useful if done right. Topics covered include: - Why many blockchain projects would be better off using a database - The disconnect between crypto culture and enterprise software expectations - What DAOs are designed to do, and why they often fail - How tokenomics influences system behavior and project outcomes - The problems with wallet UX and self-custody for regular users - Practical use cases for NFTs beyond digital art - What decentralization actually means for users and developers - The technical and operational limits of current blockchain infrastructure Vlad also talks about his work building blockchain systems for supply chains, how upcoming regulations like the EU Cyber Resilience Act may affect the space, and what kind of changes are needed for Web3 to be more reliable and useful. You'll also hear: - Why crypto infrastructure is still hard to scale and secure - How token distribution often leads to trust and governance issues - What Web3 can learn from open source and traditional software development - Where blockchain, IoT, and AI might start to overlap in the future This episode is for developers, product teams, and anyone trying to understand where Web3 stands today, and what needs to happen for it to move forward. --- # Inside the Yocto Project's Evolving Tooling: SBOMs, SPDX 3.0, and Secure Embedded Systems URL: https://vpetersson.com/podcast/s02e09/ Date: 2025-05-07 Description: In this deep-dive conversation, I sit down with Joshua Watt (Garmin) and Ross Burton (ARM) to explore how the Yocto Project is redefining how we build, secure, and maintain embedded Linux systems at scale. In this deep-dive conversation, I sit down with Joshua Watt (Garmin) and Ross Burton (ARM) to explore how the Yocto Project is redefining how we build, secure, and maintain embedded Linux systems at scale. Drawing on their years of experience contributing to Yocto's technical steering and core infrastructure, Joshua and Ross walk through why build-time Software Bill of Materials (SBOMs) offer unmatched reliability, how SPDX 3.0 enables deeper license and supply chain visibility, and what it really takes to keep embedded products secure across decade-long lifecycles. We get into the practical and architectural challenges behind modern embedded development: - Why shipping Raspberry Pi OS at scale is a ticking time bomb (and what to do instead) - What Yocto actually is, and why it's not just another Linux distribution - How build-time SBOMs outperform post-hoc scanners for audit and incident response - What VEX metadata is, and how it improves real-world vulnerability triage - How SPDX 3.0 enables granular license tracking and nested component relationships - Why OTA updates should be a design-time decision, not an afterthought Joshua also shares what it took to build Yocto's native SBOM tooling (`create-spdx`), and how the project is adapting to new pressures from regulation, such as the Cyber Resilience Act (CRA), and increasingly security-aware supply chains. We also cover: - Managing SBOMs across multi-layer BSP stacks and opaque vendor binaries - Using BitBake's hashserver to enforce reproducible, compliant builds - The philosophical shift SBOMs are triggering in the embedded world - What's next for SPDX, Yocto security tooling, and vulnerability automation The episode wraps with advice for embedded developers trying to get started with SBOMs in Yocto, from common misconceptions to integration tips and where to plug into the community. Whether you're a firmware engineer, security lead, or building connected products that need to survive for 10+ years, this episode is packed with battle-tested insight from engineers shaping the tools behind secure embedded Linux. --- # Startup Founder Lessons on Scaling Teams, Systems, and Culture with Kevin Henrikson URL: https://vpetersson.com/podcast/s02e08/ Date: 2025-04-23 Description: Learn how to scale your startup from 10 to 1500 people with Kevin Henrikson (Zimbra, Microsoft, Instacart). Discover proven strategies for managing acquisitions, building distributed teams, and creating scalable engineering cultures. Essential insights for technical founders and CTOs. In this deep-dive conversation, I sit down with Kevin Henrikson to explore what it really takes to scale technical teams, ship faster, and survive the transition from startup chaos to operating at enterprise scale. Drawing on his experience across Zimbra, Microsoft, and Instacart, Kevin shares lessons that most startup founders only learn the hard way. From being turned down for a coding role and later managing the team that rejected him, to leading engineering through acquisitions and hypergrowth, he breaks down how systems, structure, and culture evolve when a company goes from 10 people to 1500. We get into the operational and cultural challenges behind some of the most common startup transitions: - Why acquisitions often fail due to "organ rejection" and how to avoid it - What changes when a founder becomes an advisor inside a large org - How a simple Friday shipping cadence transformed Outlook Mobile's engineering culture - Why scaling isn't just about hiring, it's about systematizing decision-making and trust - What breaks first when demand suddenly explodes, as it did at Instacart during COVID Kevin also shares his approach to buying SaaS companies and applying AI and automation to operate them more efficiently with fewer people and more resilience. We also cover: - The real role of a CTO at scale (hint: it's mostly not about tech) - How to build teams across geographies without creating silos - Why code, not people, should define your infrastructure and processes - The value of having a clear North Star in operational decision-making The episode closes with a look at how systems thinking and automation are reshaping what's possible in modern engineering orgs and what founders should pay attention to before things start to break. Whether you're a technical founder, CTO, or operator building your first team or scaling your fifth, this conversation is full of practical insight from someone who's been through every stage. --- # Reimagining CI/CD and Engineering Culture at Scale with Vlad A. Ionescu URL: https://vpetersson.com/podcast/s02e07/ Date: 2025-04-11 Description: In this deep-dive conversation, I sit down with Vlad Ionescu, founder and CEO of Earthly, to unpack the evolution of CI/CD, why developer experience is broken, and what it will take to bring sanity back to software delivery at scale. **Update:** Since this episode went live, Earthly [announced](https://earthly.dev/blog/shutting-down-earthfiles-cloud/) that they ending their investment in the Earthly open source project. Efforts are being invested into having the community taking over the open source project. In this deep-dive conversation, I sit down with Vlad Ionescu, founder and CEO of [Earthly](https://earthly.dev), to unpack the evolution of CI/CD, why developer experience is broken, and what it will take to bring sanity back to software delivery at scale. From Google's internal build systems to the chaotic sprawl of modern pipelines, Vlad draws on his background at FAANG and Earthly to highlight why most teams are stuck with fragmented tooling, unpredictable environments, and compliance bottlenecks that only get worse with scale. We get into the core challenges of today's SDLC: - CI that is invisible but critical and riddled with governance gaps - Why "run it locally" is still a dream for most teams - The trust and tooling mismatch between platform and security teams and app developers - The hidden risk of blindly using plugins and actions without audit trails Vlad shares how Earthly's new product, Lunar, aims to bridge these gaps by bringing monitoring and policy enforcement to the pre-prod lifecycle, much like observability did for production. We also dig into: - The tension between agility and standardization in microservices - How remote and hybrid work affect engineering culture and accountability - Why most companies do not suffer from lack of rules but from lack of clarity and enforcement The conversation rounds out with a sharp look at AI in the software lifecycle. What happens when LLMs generate entire products, and what are the risks of skipping over code comprehension in favor of velocity? Vlad's take: without guardrails, we are heading toward a future of tech debt at hyperspeed. Whether you are in DevOps, platform engineering, or just tired of pushing CI fixes at 2 a.m., this is a real-world conversation on building smarter, safer, and more scalable developer workflows. --- # Europe's Battle for Tech Sovereignty: Why OpenStack Matters URL: https://vpetersson.com/podcast/s02e06/ Date: 2025-03-28 Description: In this timely conversation with Johan Christenson, founder of Cleura and board member of OpenInfra (overseeing Kata Containers and OpenStack), we dive into Europe's growing urgency around digital sovereignty and what it will actually take to build a competitive, homegrown cloud ecosystem. Johan, a long-time advocate for open source infrastructure, pulls back the curtain on why progress has been so slow and where things can shift. In this timely conversation with Johan Christenson, founder of [Cleura](https://cleura.com) and board member of OpenInfra (overseeing Kata Containers and OpenStack), we dive into Europe's growing urgency around digital sovereignty and what it will actually take to build a competitive, homegrown cloud ecosystem. Johan, a long-time advocate for open source infrastructure, pulls back the curtain on why progress has been so slow and where things can shift. We kick off by looking at Europe's deep dependence on U.S. cloud providers, and the real-world risks that come with it. From pricing power and data availability to political influence and tech autonomy, Johan explains how this imbalance affects everything from government policy to startup growth. A major part of our conversation breaks down the key blockers that have kept Europe stuck: - Procurement systems that favor incumbents over local innovation - Long-standing vendor lock-in across schools, agencies, and enterprises - A steady brain drain of top talent to non-European tech companies - Innovation programs that reward optics over sustainable outcomes - The sheer complexity of building a scalable, fully independent cloud stack Johan explains the uphill battle smaller providers face when going up against hyperscalers — not just on price, but on the range and depth of services they offer. He draws a sharp contrast between basic infrastructure and the fully integrated ecosystems that dominate the market. We explore his experience building on OpenStack, including: - Why his company committed early to open infrastructure - Lessons from over a decade of contributing to and operating at scale - How complexity, resilience, and governance affect platform choices - Why internal capability-building matters more than plug-and-play simplicity From there, we expand into bigger-picture ideas: how European vendors could collaborate more effectively, the role of technical standards in shaping markets, and how cloud-native tools like Kubernetes, if used thoughtfully, might help smaller players compete. The episode wraps with a sharp take on Europe's regulatory landscape. Johan argues that the problem isn't a lack of rules, but the lack of enforcement. He walks through how the disconnect between policy ambitions and operational reality slows down even the most well-intentioned builders. If you're thinking seriously about digital independence, infrastructure sovereignty, or Europe's place in the global tech stack, this conversation is both a clear-eyed reality check and a grounded look at where change could come from. For more on this topic, check out these related articles (in Swedish): - [Ge Sverige ett digitalt beredskapslyft](https://www.di.se/debatt/ge-sverige-ett-digitalt-beredskapslyft/) - [Europa behöver ett digitalt airbusprojekt](https://www.di.se/debatt/europa-behover-ett-digitalt-airbusprojekt/) --- # Physical Pen Testing Secrets: Covert Building Infiltration Explained URL: https://vpetersson.com/podcast/s02e05/ Date: 2025-03-14 Description: I delve into the fascinating world of physical penetration testing with expert Warren Houghton. This eye-opening episode reveals the techniques and tools used by professional pen testers to bypass building security systems and gain unauthorized access to secure facilities. Warren shares his real-world experiences breaking into banks, arenas, and corporate buildings, explaining everything from badge cloning and lock picking to social engineering tactics that exploit human psychology. Learn about the vulnerabilities in common access control systems, why HID proximity cards should be avoided, and why security awareness among staff is the most critical defense against physical breaches. Whether you're responsible for facility security or simply curious about how physical pen testers operate, this episode offers rare insights into the covert methods used to test and improve physical security postures. In this captivating conversation with Warren Houghton, we explore the secretive world of physical penetration testing. Warren, an experienced security professional, shares his expertise in testing and bypassing physical security measures that protect sensitive facilities and assets. We begin by discussing the fundamentals of physical penetration testing and how it differs from digital security assessments. Warren explains his methodical approach to evaluating building security, from initial reconnaissance to execution, and how he documents vulnerabilities for clients to address. A significant portion of our discussion focuses on the technical tools and techniques used in physical penetration testing: - Badge cloning technologies and vulnerabilities in common access control systems - Lock picking tools and techniques, including the use of specialized tools for different scenarios - Under-the-door tools and methods for bypassing door sensors - The effectiveness of tailgating as an entry method - The vulnerabilities of magnetic locks and how they can be compromised Warren shares fascinating war stories from his career, including breaking into: - A bank in Amsterdam where he successfully accessed the stock trading floor - An arena with inadequate security measures - Corporate buildings with sophisticated access control systems Perhaps most intriguing is Warren's deep dive into social engineering tactics. He explains how building rapport with targets like receptionists and security guards is often more effective than technical approaches. Warren demonstrates how creating a sense of trust and familiarity can lead people to willingly provide access to secure areas, highlighting the psychological aspects of security breaches. The conversation takes an important turn toward security recommendations, with Warren emphasizing that security awareness among staff is the single most critical defense against physical breaches. He explains why: - Staff should understand they are part of the security posture - Simple practices like removing badges when leaving the office significantly improve security - Investigating security alarms is essential rather than dismissing them - Certain access control technologies (particularly HID proximity cards) should be avoided We also discuss the importance of proper encryption keys for access cards, with Warren explaining that many organizations use default credentials that can be easily exploited. He provides practical advice for improving physical security, including the use of custom encryption keys and tamper-resistant readers. The episode concludes with Warren's perspective on the balance between technical security measures and human awareness, suggesting that even the most sophisticated systems can be compromised if staff aren't properly trained and vigilant. For anyone responsible for facility security or interested in understanding physical security vulnerabilities, this episode provides rare insights into the methods used by professional penetration testers and practical steps to enhance protection against unauthorized access. --- # Balena, IoT Security, and the Future of Connected Devices URL: https://vpetersson.com/podcast/s02e04/ Date: 2025-02-28 Description: I sit down with Marc Pous of Balena to explore the ever-evolving world of IoT, from remote management and fleet updates to security legislation and software bills of materials (SBOMs). Marc reveals how Balena pioneered container-based deployments on devices like Raspberry Pi, explains why over-the-air (OTA) updates are critical for any hardware project, and offers insights into upcoming regulations such as the Cyber Resilience Act (CRA). Discover why IoT is quickly becoming "just connected products," how businesses can adapt to this new era, and why it's time to rethink everything from SD cards to DevOps workflows. Whether you're an IoT veteran or curious about the future of connected tech, this episode offers a deep dive into managing devices at scale and keeping them secure. In this insightful conversation with Marc Pous from Balena, we explore the evolving landscape of IoT and connected devices. Marc, who has been deeply involved in the IoT space since 2007, brings a wealth of experience from his journey through academic research, entrepreneurship, and his current role at Balena, where he's approaching his fifth anniversary. We begin by discussing Balena's core mission: enabling developers to manage fleets of IoT devices at scale. Marc explains how Balena helps companies handle everything from remote management to over-the-air updates for hundreds of thousands of devices. The platform's unique approach focuses on Linux devices running Balena OS, which exclusively runs Docker containers, bringing modern DevOps practices to the embedded world. A significant portion of our discussion centers around the technical architecture behind Balena's platform: - The use of Yocto for building BalenaOS - Support for over 100 different ARM devices and x86 platforms - The automated testing infrastructure (Autokit) that ensures reliable updates - The importance of secure boot and full disk encryption - The role of Docker containers in simplifying device management We dive deep into the challenges of maintaining IoT devices at scale, particularly the critical nature of over-the-air (OTA) updates. Marc shares real-world examples of companies learning the hard way why proper update mechanisms are essential, including a cautionary tale of a company that had to physically replace 500 devices due to inadequate update capabilities. The conversation takes an important turn toward security and compliance, particularly the upcoming EU Cyber Resilience Act (CRA). Marc emphasizes how this legislation will fundamentally change how companies approach IoT security, requiring: - Proper security assessments - Software Bills of Materials (SBOMs) - Regular security updates - Clear product lifecycle management We also explore the evolution of IoT business models, discussing how companies are adapting to the shift from one-time sales to ongoing service relationships. Marc provides valuable insights into why some IoT projects succeed while others fail, emphasizing the importance of clear return on investment and sustainable business models. The episode concludes with Marc's interesting perspective on the future of IoT, suggesting that the term "IoT" itself might disappear as connectivity becomes an expected feature rather than a special category. This reflects a broader maturation of the industry, where the focus shifts from the novelty of connection to the actual value provided by smart, connected products. For developers and organizations looking to get started with modern IoT development, Marc recommends exploring Balena's open source projects and documentation at [balena.io](https://www.balena.io). Whether you're using Balena Cloud or Open Balena, the platform offers a proven path to managing connected devices at scale while maintaining security and reliability. If you found this episode interesting, you might also enjoy my blog post [Yocto, RockPi and SBOMs: Building Modern Embedded Linux Images](/2025/02/21/yocto-rockpi-and-sboms/). --- # Dustin Kirkland on Chainguard, Zero-CVE Containers, and Supply Chain Security URL: https://vpetersson.com/podcast/s02e03/ Date: 2025-02-14 Description: In this episode, I sit down with Dustin Kirkland to discuss the critical topic of supply chain security and the innovative work being done at Chainguard. We delve into the concept of zero-CVE containers, exploring how this approach is revolutionizing container security. Dustin shares insights on modern supply chain threats, the importance of software bill of materials (SBOM), and practical strategies for maintaining secure container environments. The conversation covers both the technical challenges and solutions in today's rapidly evolving security landscape. In this engaging conversation with Dustin Kirkland, we explored the fascinating world of container security and supply chain integrity through the lens of Chainguard's innovative approach. Dustin, with his extensive background spanning IBM, Canonical, Google, and now Chainguard, brings a wealth of experience in open source, security, and enterprise software development. We began by discussing Chainguard's unique position in the container security landscape. Unlike traditional Linux distributions, Chainguard takes a different approach by focusing on building and maintaining "zero-CVE" containers - container images that are continuously updated to eliminate known vulnerabilities. This involves an impressive automated pipeline that can detect and patch vulnerabilities within hours of upstream fixes becoming available. A significant portion of our discussion centered around the technical architecture behind Chainguard's container security approach: - The build system that pulls directly from source repositories rather than using pre-built binaries - Their unique approach to package management using APK format - The automated vulnerability remediation process that exceeds FedRAMP standards - The implementation of software bill of materials (SBOM) for complete transparency - The use of Sigstore and Cosign for container signing and verification We also delved into the challenges of maintaining rolling releases versus static releases, particularly in enterprise environments. Dustin shared valuable insights about how Chainguard manages to provide both security and stability through their approach of continuous updates rather than periodic major upgrades. The conversation took an interesting turn when we discussed open source sustainability and the evolution of business models around open source software. Dustin shared his perspective on how the industry has matured from the early days of Linux to today's cloud-native landscape. Some key takeaways from our discussion: 1. The importance of proactive security measures in container environments 2. How automated tooling can dramatically reduce the time to patch vulnerabilities 3. The role of SBOMs in modern software supply chain security 4. The balance between security, stability, and usability in container images 5. The evolution of open source business models and sustainability For developers and platform engineers interested in learning more about Chainguard's approach, Dustin recommended visiting [images.chainguard.dev](https://images.chainguard.dev) where you can explore their public container registry and documentation. This episode provides valuable insights for anyone interested in container security, supply chain integrity, or the evolution of secure software delivery in the cloud-native era. --- # Pentesting 101: Hacking Legally with Warren Houghton URL: https://vpetersson.com/podcast/s02e02/ Date: 2025-01-31 Description: In my conversation with Warren Houghton, he shows exactly how a penetration test unfolds: starting with scoping and permission paperwork, then moving into reconnaissance and tooling. He demonstrates how a tester uses Kali Linux alongside Nmap for port scanning, Metasploit for exploiting vulnerabilities, and Burp Suite for intercepting and manipulating web traffic. He also highlights the risks of overlooked exposures—like a public .git directory or outdated WordPress plugins—and stresses how secure configurations and network segmentation can prevent lateral movement. Throughout, he emphasizes the constant need for learning and vigilance against ever-evolving threats. In my conversation with Warren Houghton, I gained a thorough understanding of penetration testing from both a strategic and technical standpoint. We started with the scoping process, which sets the stage for any successful test. I learned that having a clear agreement about what systems, applications, and IP addresses can be targeted is not just a legal safeguard; it also helps avoid accidentally bringing down critical services. Warren highlighted how testers confirm authorization by collecting signed documents, which eliminates any risk of unauthorized hacking activities. Once the paperwork is settled, Warren described how he begins reconnaissance by using [Nmap](https://nmap.org) to scan for open ports and fingerprint the services running behind them. This step reveals the "attack surface," giving a tester an overview of what might be vulnerable. He then uses the [Metasploit Framework](https://www.metasploit.com) to match those discovered services with known exploits, adjusting configurations and payloads to see if a target can be compromised. Warren stressed that even a single exposed service or overlooked legacy system can provide a foothold for further attacks. Proper network segmentation becomes critical at this point because once an attacker gains any level of access, it is surprisingly easy to move laterally if different internal networks are not correctly isolated. We also explored how web application assessments often involve [Burp Suite](https://portswigger.net/burp), which intercepts traffic between the browser and the web server. Warren demonstrated how simple it is to bypass client-side JavaScript restrictions by modifying HTTP requests directly. This technique can reveal missing server-side checks, insecure file uploads, or subpar password handling. Warren also talked about finding an exposed `.git` directory that leaked source code and hardcoded credentials, opening the door to extensive compromise. He shared a few anecdotes about real-world breaches that began with these seemingly minor oversights, including one where a misconfigured IoT device acted as a springboard into a corporate network. Our discussion then shifted to containerized environments, especially those running [Docker](https://docs.docker.com). Warren pointed out that containers, when configured with strict isolation, can limit an attacker's reach. However, he also noted that developers sometimes map the Docker socket into a container for convenience, effectively granting root-level access to the host if exploited. This tension between convenience and security reappeared throughout the conversation, reminding me that human error is often the weakest link in the chain. As we wrapped up, I asked Warren about his favorite success stories and the times he encountered unexpected barriers. He recalled instances where small configuration tweaks stopped his attacks cold, underscoring how even basic security best practices can go a long way. On the flip side, he mentioned multiple tests where organizations missed routine patching or left default credentials in place, giving him unfettered access in minutes. Ultimately, our talk reinforced the idea that continuous learning, robust patch management, and persistent monitoring are essential. Even advanced tools like [Kali Linux](https://www.kali.org/docs) rely on the user's knowledge, creativity, and ongoing vigilance to keep pace with evolving threats. I left the conversation with a renewed appreciation for how methodical, yet inventive, penetration testing can be in strengthening security at every level. --- # A deep dive into the SBOM format SPDX URL: https://vpetersson.com/podcast/s02e01/ Date: 2025-01-17 Description: In my conversation with Kate Stewart and Gary O'Neall, we explore the evolution and impact of SPDX in software transparency. From its origins in license compliance to its current role in security and vulnerability tracking, we unpack how this open standard is shaping modern software development practices. The discussion reveals fascinating insights into SBOM generation challenges and how SPDX 3.0 is being designed to meet the demands of today's CI/CD environments. In this episode, I speak with Kate Stewart from the Linux Foundation and Gary O'Neall, a long-time SPDX contributor, about the evolution of SPDX and its role in software transparency. We discuss how SPDX started as a tool for tracking open-source license compliance and continues to address broader needs in security and vulnerability management. Kate and Gary walked through the technical challenges teams face when generating accurate SBOMs, including handling circular dependencies and dealing with uncertainty in software components. They shared practical examples from their work with various organizations and explained how these challenges influenced the development of SPDX tools and specifications. We explored current efforts to integrate SBOM generation into build systems, looking at specific examples from the [Zephyr](https://www.zephyrproject.org/) and [Yocto](https://www.yoctoproject.org/) projects. The discussion covered ongoing work to implement build-time SBOM generation for the Linux kernel, highlighting both the technical approach and its practical benefits for development teams. The conversation then turned to the growing regulatory requirements around SBOMs, particularly in safety-critical systems. Kate and Gary explained how SPDX 3.0 is being developed to handle these requirements while supporting modern CI/CD pipelines. They described the technical considerations behind maintaining compatibility with existing tools while adding features for new use cases. SPDX remains an open, community-driven project that continues to evolve with industry needs. Whether you're dealing with compliance, security vulnerabilities, or supply chain transparency, this episode provides concrete insights into how SPDX can help address these challenges in your software development workflow. Related episodes: - [Unveiling SBOMs: Insights from Allan Friedman of CISA](/podcast/s01e16/) - A deep dive into SBOM policy and implementation with 'the father of SBOMs' - [SBOMs, CycloneDX, and Software Security with Steve Springett](/podcast/s01e22/) - Exploring CycloneDX with its creator and lead architect --- # All things ZFS and FreeBSD with Allan Jude URL: https://vpetersson.com/podcast/s01e25/ Date: 2024-12-02 Description: I explore the technical depths of ZFS and FreeBSD with Allan Jude, diving into everything from filesystem architecture to operating system development. From copy-on-write design to advanced storage capabilities, we unpack what makes these technologies crucial for modern infrastructure. In this episode, I'm joined by Allan Jude, a distinguished FreeBSD developer and ZFS expert, to explore the fascinating world of advanced storage systems and operating system architecture. Allan's extensive contributions to both FreeBSD and ZFS offer unique insights into how these technologies shape modern infrastructure. We start with ZFS's architectural foundations. What particularly caught my attention was Allan's explanation of how copy-on-write mechanisms transform data integrity and storage management. His breakdown of ZFS's self-healing capabilities and data verification approaches reveals why it remains crucial for enterprise storage solutions. The conversation gets especially interesting when we dive into FreeBSD's networking stack. Allan shares insights into why major technology companies trust FreeBSD for their mission-critical operations, backing up the discussion with real-world examples from his extensive experience. His practical deployment strategies bridge the gap between theoretical knowledge and real-world applications. I was particularly intrigued by our discussion of optimizing ZFS configurations and managing storage pools effectively. Allan's perspective on leveraging FreeBSD's security features and his thoughts on the future of storage systems and operating system development show just how much innovation is happening in this space. If you're interested in storage infrastructure, operating system internals, or enterprise systems, you'll find plenty of practical insights here. Allan brings both deep technical knowledge and years of hands-on experience to the discussion, making complex storage and OS concepts accessible while maintaining their technical depth. --- # Hacking airplanes, ships and IoT devices with Ken Munro URL: https://vpetersson.com/podcast/s01e24/ Date: 2024-11-04 Description: I dive into the world of GPS systems and maritime security with Ken Munro, exploring the vulnerabilities in critical infrastructure. We discuss everything from different types of GPS systems to the alarming ease of compromising ships remotely. Ken shares eye-opening insights from his penetration testing experience and the implications for global security. In this episode, I'm joined by Ken Munro for an eye-opening discussion about security vulnerabilities in transportation and IoT systems. Ken's work in penetration testing has revealed some seriously concerning issues in systems we rely on daily, and I was eager to dig into the details. We start by exploring GPS systems - not just the US Global Positioning System that most people know, but the whole family of satellite-based navigation systems. Ken walks me through some shocking vulnerabilities he's discovered, particularly in maritime systems. What really caught my attention was his demonstration of how relatively easy it is to compromise these critical systems. The conversation takes a fascinating turn when we discuss IoT security. Ken shares some incredible stories from his penetration testing work, showing how seemingly innocent devices can become serious security risks. We explore real examples of vulnerabilities he's found, and what makes these systems so challenging to secure properly. I particularly enjoyed our discussion about the broader implications of these security issues. We dive into how these vulnerabilities could affect global trade and security, and what needs to change in the industry. Ken brings up some thought-provoking points about the balance between innovation and security, especially in transportation systems. If you're interested in cybersecurity, IoT, or transportation systems, you'll find plenty of technical insights here. Ken has a unique way of explaining complex security concepts through real-world examples, making the implications clear without getting lost in technical jargon. --- # From Passwords to Passkeys: Exploring the Future of Authentication with Massi Gori URL: https://vpetersson.com/podcast/s01e23/ Date: 2024-11-04 Description: I explore the changing landscape of authentication with Massi Gori, diving into practical applications of FIDO2 and modern security solutions. We discuss how technologies like passkeys, OpenSSH certificates, and Vault are reshaping enterprise security, with real-world examples from Massi's extensive experience. Today I'm joined by Massi Gori for a deep dive into modern authentication systems. Having worked extensively with these technologies, I was particularly excited to get Massi's insights on where we're heading with authentication and access control. We start by exploring the evolution from traditional passwords to passkeys. Massi breaks down the technical aspects of FIDO2, but what I found most valuable was his practical experience implementing these systems in enterprise environments. We discuss the real challenges organizations face when transitioning to modern authentication methods, and how to overcome them. The conversation gets particularly interesting when we dive into OpenSSH certificates and Vault. Massi shares some fascinating insights about how these tools can work together to create robust authentication systems. What really caught my attention was his approach to balancing security with usability - something that's often overlooked in authentication discussions. I especially enjoyed our discussion about the future of authentication. We explore how passkeys are changing the game for both users and organizations, and what this means for traditional password-based systems. Massi brings up some thought-provoking points about the challenges of managing authentication at scale, and we discuss practical solutions that work in the real world. If you're dealing with authentication systems, whether in a small team or a large enterprise, you'll find plenty of actionable insights here. Massi does a great job of explaining complex security concepts while keeping the focus on practical, implementable solutions. --- # SBOMs, CycloneDX, and Software Security with Steve Springett URL: https://vpetersson.com/podcast/s01e22/ Date: 2024-10-21 Description: I explore the evolving world of software security and compliance with Steve Springett, diving into how SBOMs and CycloneDX are reshaping how we track and secure our software supply chains. We discuss practical approaches to implementing these tools and the real impact they're having on enterprise security. Today I'm diving into the world of Software Bill of Materials (SBOMs) with Steve Springett, one of the core working group stewards behind CycloneDX. Steve's work on standardizing how we communicate about software components is reshaping security practices across the industry. We start by exploring what makes CycloneDX different from other SBOM standards. Steve explains their pragmatic approach to design, focusing on automation and real-world usability. What really caught my attention was how they're tackling the challenge of dependency tracking and software supply chain security - issues I've wrestled with myself in various projects. The conversation takes an interesting turn when we discuss different types of SBOMs within CycloneDX. Steve walks me through how their approach allows for creating a single bill of materials by linking different components together. Even in small deployments, this leads to multiple SBOMs, and we explore the practical implications of managing this complexity. I was particularly interested in Steve's vision for the future of software security and compliance. We discuss how organizations could potentially communicate autonomously through standardized SBOMs, and the real-world impact this could have on security practices. Steve shares some fascinating insights about Project Koala, which is taking a similar approach to CycloneDX. If you're working in software development or security, you'll find plenty of practical takeaways here. Steve brings deep expertise in software supply chain security while keeping the discussion grounded in real-world applications. Whether you're just starting with SBOMs or looking to improve your existing security practices, this conversation covers both the fundamentals and advanced concepts you need to know. Related episodes: - [A deep dive into the SBOM format SPDX](/podcast/s02e01/) - Exploring SPDX with Kate Stewart and Gary O'Neall - [Unveiling SBOMs: Insights from Allan Friedman of CISA](/podcast/s01e16/) - A deep dive into SBOM policy with 'the father of SBOMs' --- # Uncovering Firmware Security: A Deep Dive with Binarly's Philipp Deppenwiese URL: https://vpetersson.com/podcast/s01e20/ Date: 2024-09-22 Description: I dive deep into firmware security with Philipp Deppenwiese from Binarly, exploring how attestation ensures system integrity from boot to runtime. We discuss their groundbreaking work in finding BIOS vulnerabilities and how modern computing systems can better protect against firmware-level attacks. Today I'm joined by Philipp Deppenwiese from Binarly for a deep technical dive into firmware security. If you've been following the security space, you might know Binarly for their impressive work uncovering BIOS vulnerabilities like PixieFail and LogoFail. We start by exploring attestation - a crucial yet often overlooked aspect of secure computing. Philipp breaks down how it differs from traditional secure boot mechanisms, particularly when it comes to verifying what code is actually running on a system. What I found particularly interesting was his explanation of how TPMs can facilitate attestation, creating an unbreakable chain of trust from boot to runtime. The conversation gets really interesting when we discuss the practical challenges of implementing secure boot in different environments. Philipp shares his insights on why it can be particularly tricky in Linux compared to Windows, and we explore the entire boot flow from firmware to kernel. His explanation of how certain technologies enable secure computation on cloud stacks was eye-opening. One of the most valuable parts of our discussion focuses on transparency in modern computing. Philipp points out how many of us "just run things" without questioning the underlying software or firmware, and we discuss why this can be dangerous. We also get into Binarly's innovative solutions using TPMs and attestation, which I think shows a promising direction for the future of system security. If you're interested in firmware security, TPMs, or how modern systems establish trust from the ground up, you'll find plenty of practical insights here. Philipp brings deep technical knowledge but explains complex concepts in a way that makes them accessible without losing the technical depth. --- # RFID Hacking with Iceman: Exploring RFID Security URL: https://vpetersson.com/podcast/s01e21/ Date: 2024-09-22 Description: I explore the fascinating world of RF hacking with Iceman, diving into everything from Bluetooth signal capture to mobile credential vulnerabilities. From Proxmark experimentation to practical applications, we unpack what makes RFID security both challenging and crucial. In this episode, I'm joined by Iceman, a renowned expert in RFID and NFC hacking, to explore the fascinating world of RF security. Iceman's extensive contributions to the Proxmark platform, particularly through his "[Iceman Fork](https://github.com/RfidResearchGroup/proxmark3)," offer unique insights into the capabilities and vulnerabilities of RFID systems. We start with Iceman's journey in RF hacking and his early work with Proxmark. What particularly caught my attention was his explanation of how the platform has evolved, extending its capabilities to support a wider range of RFID tags and protocols. His commitment to open-source development and knowledge sharing reveals the collaborative nature of the RF hacking community. The conversation gets especially interesting when we dive into Bluetooth credentials and their vulnerabilities. Iceman demonstrates how tools like Proxmark v4 and custom firmware can be used to clone contactless payment cards, highlighting both the power and responsibility that comes with these capabilities. I was particularly intrigued by our discussion about getting started in RF hacking. Iceman's suggestion to begin with basic devices like the $30 Proxmark v4 shows how accessible this field can be. His approach to teaching complex concepts makes RF security approachable while maintaining its technical depth. If you're interested in RFID security, hardware hacking, or the intersection of physical and digital security, you'll find plenty of practical insights here. Iceman brings both deep technical knowledge and years of hands-on experience to the discussion, making complex RF concepts accessible while maintaining their technical sophistication. --- # Unpacking Docker's Journey: Justin Cormack, on DevOps, Containerization, and the Future of Wasm URL: https://vpetersson.com/podcast/s01e19/ Date: 2024-09-09 Description: In this episode, I sit down with Justin Cormack, Docker's CTO, to explore the evolution of Docker from its cloud-native roots to its current innovations. We dive into the real challenges of container adoption, discuss Docker Hub's growth journey, and explore how WebAssembly is shaping the future of development. I'm excited to share my conversation with Justin Cormack, Docker's CTO. Justin has been at the heart of Docker's evolution, and today we dig into both the technical and strategic decisions that have shaped containerization as we know it. We kick off by exploring Justin's early days with Docker, which sets up a fascinating discussion about how Docker has transformed application deployment. The whole "containers vs VMs" debate comes up, and Justin explains why this comparison misses the point - it's really about making application packaging and deployment more accessible to developers. Our conversation takes us through Docker's journey from its early startup days to its current position in the cloud-native landscape. Justin shares some great insights about the challenges they faced in the early market and how they managed to break through, particularly with early cloud adopters who saw containers' potential. We also get into the rationale behind Docker's decision to start charging for Docker Desktop - a move that stirred up quite a bit of discussion in the tech community. The technical depth really picks up when we dive into Docker Hub's infrastructure. Justin walks me through how they've scaled their global service, from the early days of headline-making outages to today's robust platform serving millions of developers. We also explore Docker's evolution in image security - it's fascinating to hear how they moved from basic cryptographic signatures to the V2 format with proper content hashing. One of the highlights is our discussion about Docker Scout, their latest innovation. Justin explains how it generates Software Bill of Materials (SBOMs) and handles real-time vulnerability tracking. This leads us into an engaging exploration of WebAssembly (Wasm) and its potential impact on both server-side and browser-based applications. If you're working with containers, interested in the evolution of cloud-native tech, or curious about where development tools are heading, you'll find plenty of practical insights here. Justin brings a unique perspective from his years at Docker, and we cover everything from technical architecture decisions to the broader impact on how we build and deploy software today. --- # Exploring the Future of AI: Luke Marsden Unveils Helix and the Open Source Revolution URL: https://vpetersson.com/podcast/s01e18/ Date: 2024-08-26 Description: I explore the world of practical AI deployment with Luke Marsden, diving into Helix, an open-source platform that enables secure, local AI implementation. We discuss how Helix works with tools like Olama and Rag, and examine the real implications of decentralized AI decision-making. In this episode, I'm joined by Luke Marsden to explore the practical side of AI deployment and his work on Helix. Luke brings deep expertise in making AI accessible and secure for businesses, and I was particularly interested in his approach to open-source AI solutions. We start with a clear breakdown of large language models (LLMs), with Luke explaining their core concepts in practical terms. What really caught my attention was his perspective on the evolution from research curiosity to practical business tool, especially following ChatGPT's impact on the industry. The conversation gets particularly interesting when we dive into the open-source versus proprietary AI debate. Luke makes a compelling case for open-source models like Meta's LLaMA and Mistral, explaining how they're catching up to proprietary solutions while offering better control over data privacy - a crucial concern for many businesses. I especially enjoyed our hands-on demo of Helix, where Luke shows how businesses can deploy and manage AI models on their own infrastructure. We explore everything from creating custom chatbots to integrating AI with existing systems, all while maintaining data privacy and control. The platform's ability to run entirely on local infrastructure or cloud-based GPUs demonstrates a practical solution to a common business challenge. If you're interested in implementing AI in your organization, particularly with a focus on security and control, you'll find plenty of practical insights here. Luke brings both technical depth and real-world experience to the discussion, making complex AI concepts accessible without losing their technical substance. --- # Unveiling SBOMs: Insights from Allan Friedman of CISA URL: https://vpetersson.com/podcast/s01e16/ Date: 2024-07-29 Description: I explore the critical role of Software Bill of Materials (SBOMs) with Allan Friedman from CISA, diving into how these tools are reshaping software supply chain security. We discuss everything from practical implementation challenges to their impact on international policy, offering insights for developers and security professionals alike. In this episode, I'm joined by Allan Friedman from CISA for an in-depth exploration of Software Bill of Materials (SBOMs). Allan brings unique insights from his work at CISA, where he's been instrumental in shaping how we approach software supply chain security. We begin by discussing CISA's role in cybersecurity, with Allan explaining their mission of "defending today and securing tomorrow." What particularly interests me is how they balance immediate threat response with building more secure infrastructure for the future. Allan's explanation of CISA's international partnerships and their collaboration with other US government agencies provides valuable context for understanding the broader security landscape. The conversation gets especially interesting when Allan draws parallels between software transparency and food labeling. We dive into the technical details of SBOM formats like CycloneDX and SPDX, exploring their origins and key differences. I found his insights into the communities supporting these formats particularly valuable for understanding their practical applications. We tackle the real-world challenges of implementing SBOMs, from automation issues to maintaining accuracy in dynamic software environments. Allan shares practical advice for organizations starting their SBOM journey, and we explore related tools like VEX (Vulnerability Exploitability Exchange) and their role in securing software supply chains. If you're involved in software development or security, you'll find plenty of practical takeaways here. Allan brings both policy expertise and technical understanding to the discussion, making complex security concepts accessible while maintaining their technical depth. Links: - [SBOM-o-Rama Winter 2024](https://www.cisa.gov/news-events/events/sbom-rama-winter-2024) - Episode Sponsor: [sbomify](https://sbomify.com) Related episodes: - [A deep dive into the SBOM format SPDX](/podcast/s02e01/) - Exploring SPDX with Kate Stewart and Gary O'Neall - [SBOMs, CycloneDX, and Software Security with Steve Springett](/podcast/s01e22/) - A technical deep dive with CycloneDX's creator and lead architect --- # Daniel Stenberg on Curl's Journey: From C64 Demos to Internet Transfers URL: https://vpetersson.com/podcast/s01e17/ Date: 2024-07-29 Description: I explore Daniel Stenberg's remarkable journey from coding C64 demos to creating Curl, diving into the challenges of maintaining a critical open-source project for over two decades. We discuss everything from community building to security measures, with fascinating insights into how Curl became essential internet infrastructure. In this episode, I'm joined by Daniel Stenberg, the creator of Curl, for a fascinating journey through open-source development. Daniel's path from the Commodore 64 demo scene to building one of the most widely-used tools in software development offers unique insights into both technical evolution and community building. We begin with Daniel's early days programming on the C64 and Amiga, where he cut his teeth in the demo scene. What really caught my attention was how these experiences shaped his approach to programming and eventually led to Curl's development at IBM. His journey from hobby coder to maintaining critical internet infrastructure is both inspiring and instructive. The conversation takes an entertaining turn when Daniel shares some of the most unusual support requests he's received. These stories not only highlight Curl's ubiquity but also reveal common misconceptions about open-source software. I particularly enjoyed his practical insights into managing a project that millions of developers rely on daily. We dive deep into Curl's technical evolution, discussing how Daniel approaches backward compatibility and protocol support. His explanation of the careful balance between adding new features and maintaining stability shows the real challenges of maintaining widely-used infrastructure. The discussion about preventing supply chain attacks and ensuring code integrity was especially relevant given today's security landscape. If you're interested in open-source development, maintaining critical infrastructure, or building developer communities, you'll find plenty of practical insights here. Daniel brings decades of experience maintaining one of the internet's most crucial tools, sharing both technical wisdom and practical lessons about sustainable open-source development. --- # Past, Present, and Future of Computing with Bryan Cantrill, CTO of Oxide Computer Company URL: https://vpetersson.com/podcast/s01e15/ Date: 2024-07-15 Description: I explore the evolution of computing with Bryan Cantrill, diving into his journey from developing DTrace at Sun Microsystems to revolutionizing on-premises cloud computing at Oxide. We discuss the challenges of cloud infrastructure, the impact of Moore's Law slowdown, and how integrated hardware-software design is reshaping the industry. In this episode, I'm joined by Bryan Cantrill, CTO and co-founder of Oxide Computer Company, for a deep dive into the evolution of computing technology. Bryan's journey from developing DTrace to reimagining cloud infrastructure offers fascinating insights into where our industry has been and where it's heading. We start with Bryan's groundbreaking work on DTrace at Sun Microsystems. What really caught my attention was his frustration with system observability limitations and how it drove him to develop a solution that could instrument running systems without modifying them. This approach to problem-solving - focusing on observation rather than modification - has influenced system design ever since. The conversation gets particularly interesting when Bryan shares his experiences running a public cloud on commodity hardware at Joyent. His insights into the practical challenges they faced, from OS-based virtualization with zones to adopting OpenSolaris, reveal the real complexities of building cloud infrastructure. These experiences clearly shaped his current work at Oxide. I was fascinated by Bryan's explanation of Oxide's integrated approach to hardware and software design. We explore their innovative choices, from larger fans for better cooling to developing custom service processors and operating systems. Bryan's perspective on the economic implications of the Moore's Law slowdown, particularly regarding infrastructure ownership versus rental, offers valuable insights for anyone planning their computing strategy. If you're interested in system design, cloud infrastructure, or the future of computing, you'll find plenty of practical insights here. Bryan brings decades of experience and a unique perspective on how integrated hardware-software solutions could reshape enterprise computing. --- # Unlocking Firmware Secrets with Christian Walter: BIOS Vulnerabilities & Security Insights URL: https://vpetersson.com/podcast/s01e14/ Date: 2024-07-01 Description: I explore the intricate world of firmware and hardware security with Christian Walter from 9Elements. We dive into recent vulnerabilities like LogoFail and PixieFail, examine the complexities of BIOS security, and discuss how Trusted Platform Modules (TPMs) are reshaping hardware security. In this episode, I'm joined by Christian Walter from 9Elements for a deep dive into firmware security. As the leader of their firmware development department and a key figure in the Open Source Firmware Foundation, Christian brings unique insights into the challenges and innovations in firmware security. We begin by exploring recent high-profile vulnerabilities like LogoFail and PixiFail. What particularly caught my attention was Christian's technical breakdown of these issues and their broader implications for system security. His explanation of how these vulnerabilities work reveals the complex challenges we face in securing firmware. The conversation gets especially interesting when we discuss BIOS security. Christian shares fascinating insights about code base reuse across vendors and the challenges in auditing firmware. I was particularly struck by his revelations about the lengthy disclosure periods for firmware vulnerabilities and what this means for system security. We dive deep into Trusted Platform Modules (TPMs), with Christian explaining different types and their security implications. His disclosure about how attackers could unseal Intel TPMs by reassigning pins from user space was eye-opening. We also explore 9Elements' innovative Firmware CI project, which aims to modernize firmware testing and validation. If you're interested in hardware security, firmware development, or system architecture, you'll find plenty of practical insights here. Christian brings both deep technical knowledge and practical experience, making complex firmware concepts accessible while maintaining their technical depth. --- # Exploring the Depths of Linux and Open Source Innovation with Mark Shuttleworth URL: https://vpetersson.com/podcast/s01e13/ Date: 2024-06-17 Description: I explore the evolution of Linux and open source with Mark Shuttleworth, diving into his journey from South African tech entrepreneur to Ubuntu founder. We discuss everything from the early days of digital certificates to space tourism, and how these experiences shaped his vision for Ubuntu and open source software. In this episode, I'm joined by Mark Shuttleworth for a wide-ranging discussion about Linux, open source, and the future of computing. Mark's journey from founding a certificate authority in South Africa to becoming the second private space tourist before creating Ubuntu offers a unique perspective on technology and innovation. We begin with Mark's early days in technology, exploring how his combined interest in science and business led to starting a CA company that caught Netscape's attention. What particularly fascinated me was his transition from the business world to space exploration, including his intensive training in Russia. These diverse experiences clearly influenced his approach to technology and open source. The conversation gets especially interesting when we dive into Ubuntu's origins. Mark shares his deep appreciation for Debian's technical and social framework, and how this inspired his vision to make Linux more accessible. We explore crucial decisions like adopting SystemD and introducing Snaps, with Mark offering candid insights into both the technical and community challenges these changes presented. I was particularly intrigued by our discussion of Ubuntu Core and its approach to building secure, transactional systems. Mark's explanation of how this technology could reshape IoT and desktop computing reveals a thoughtful vision for the future of secure computing. We also tackle the controversies and challenges around Snaps, with Mark providing a frank assessment of the trade-offs involved. If you're interested in open source, Linux, or the future of computing, you'll find plenty of practical insights here. Mark brings both technical depth and strategic vision to the discussion, offering a unique perspective on how open source can evolve to meet tomorrow's challenges while staying true to its collaborative roots. --- # Transforming Tech with Eben Upton: Exploring Raspberry Pi's Global Impact URL: https://vpetersson.com/podcast/s01e12/ Date: 2024-06-03 Description: I dive deep with Eben Upton into the Raspberry Pi journey, from its origins in computer science education to its impact on industrial applications. We explore everything from SD card durability to the future of RISC-V, and how Screenly leverages Pi for digital signage. In this episode, I'm joined by Eben Upton, the founder of Raspberry Pi Foundation, for a fascinating discussion about affordable computing and its impact on education and industry. Eben's journey from observing declining computer science applications at Cambridge to creating a global computing phenomenon offers unique insights into how simple ideas can transform technology education. We start with Eben's early computing experiences in the 1980s, which shaped his vision for Raspberry Pi. What particularly struck me was the story of the Pi's launch in 2012, selling an incredible 100,000 units on day one. This success story takes an interesting turn as we explore how Pi expanded beyond education into industrial applications, including my own experience with Screenly using Pi for digital signage. The conversation gets especially interesting when we dive into the challenges faced during the COVID-19 pandemic. Eben shares candid insights about the tough decisions made to prioritize OEM customers and maintain supply chains. His explanation of why they manufacture in the UK, balancing global sourcing with local assembly, reveals the practical complexities of hardware production. I was particularly intrigued by our discussion about future technologies. We explore the potential of RISC-V architecture, with Eben offering a pragmatic view of its adoption challenges. The reliability issues with SD cards - a topic I'm deeply familiar with from my work with Screenly - led to an enlightening discussion about storage solutions and the possibility of soldered-down eMMC storage in future models. If you're interested in hardware innovation, education technology, or the challenges of scaling a hardware platform, you'll find plenty of practical insights here. Eben brings both technical depth and strategic vision to the discussion, offering a unique perspective on how affordable computing can transform both education and industry. --- # Demystifying eBPF with Liz Rice: A Deep Dive into Kernel Programming and Security URL: https://vpetersson.com/podcast/s01e11/ Date: 2024-05-20 Description: I explore the transformative world of eBPF with Liz Rice, diving into how this technology is revolutionizing observability, security, and networking. From Netflix's pioneering work to Isovalent's innovations, we unpack what makes eBPF a game-changer for modern infrastructure. In this episode, I'm joined by Liz Rice, a security expert and open-source advocate, for a deep dive into the fascinating world of eBPF. Liz's expertise in kernel programming and security offers unique insights into how this technology is reshaping modern infrastructure. We start by breaking down what eBPF actually is - dynamic programming of the Linux kernel to alter its behavior. What particularly caught my attention was how this technology has evolved far beyond its original purpose of packet filtering. Liz shares her introduction to eBPF through Thomas Graf's presentation on Cilium at DockerCon 2017, while I highlight Brendan Gregg's groundbreaking work at Netflix using eBPF for network diagnostics. The conversation gets especially interesting when we explore how eBPF is revolutionizing traditional tools like IP tables. Liz explains how eBPF's efficiency makes complex tasks like network policy enforcement and zero-trust networking more achievable in modern cloud-native architectures. Her insights into combining eBPF with tools like WireGuard and IPsec for secure communication reveal the practical implications for modern infrastructure. I was particularly intrigued by our discussion of Tetragon, a Cilium project that leverages eBPF for runtime security. Liz's explanation of how it enables real-time, low-overhead monitoring and threat response showcases the practical applications of this technology. We also tackle recent supply chain security challenges, like the Log4j vulnerability, exploring how eBPF-based tools can adapt quickly to emerging threats. If you're interested in infrastructure security, kernel programming, or the future of cloud-native technologies, you'll find plenty of practical insights here. Liz brings both deep technical knowledge and practical experience to the discussion, making complex kernel concepts accessible while maintaining their technical depth. --- # Revolutionizing Firmware Updates in Linux: A Deep Dive with Experts URL: https://vpetersson.com/podcast/s01e10/ Date: 2024-05-06 Description: I explore the world of firmware updates with Richard Hughes from Red Hat and Mario Limonciello from AMD, diving into their work on the fwupd project and Linux Vendor Firmware Service (LVFS). We discuss how they're tackling the challenges of deploying updates across millions of Linux devices and what this means for system security. In this episode, I'm joined by Richard Hughes from Red Hat and Mario Limonciello from AMD to explore the fascinating world of firmware updates in Linux. Their work on the Firmware Update Project has fundamentally changed how we handle firmware updates in the Linux ecosystem. We start with Richard sharing the origin story of fwupd, which began with his work on Colorhug, a free software color sensor. What particularly caught my attention was how this seemingly simple project revealed the broader need for standardized firmware updates in Linux. Mario then adds his perspective from Dell, where they were trying to match Windows Update's capabilities for Linux users. The conversation gets especially interesting when we dive into the technical details of LVFS (Linux Vendor Firmware Service). Richard and Mario explain how they've created a centralized system that not only distributes firmware but also ensures its integrity and security. Their insights into supply chain security and the role of SBOM (Software Bill of Materials) in firmware updates reveal the complexity of modern system maintenance. I was particularly intrigued by the discussion of how major vendors like Dell, Lenovo, and HP have adopted fwupd and LVFS. The impact of Google's "Works with Chromebook" initiative on consumer device support shows how far the project has come. We also explore the challenges still facing server firmware updates and the potential role of Redfish in addressing these issues. If you're interested in system security, firmware management, or the evolution of Linux infrastructure, you'll find plenty of practical insights here. Richard and Mario bring both deep technical knowledge and years of real-world experience to the discussion, making complex firmware concepts accessible while maintaining their technical depth. --- # Mastering OpenSSF Scorecards & SBOMs with Chris Swan URL: https://vpetersson.com/podcast/s01e09/ Date: 2024-04-22 Description: I dive into the world of Software Bill of Materials (SBOMs) and OpenSSF Scorecards with Chris Swan from Atsign. We explore how these tools are transforming open source security, from dependency management to secure coding practices, and what this means for building trust in the tech industry. In this episode, I'm joined by Chris Swan from Atsign to explore the evolving landscape of open-source security. Chris's experience with end-to-end encrypted connections and his work with the Open Source Security Foundation (OpenSSF) offers unique insights into how we can better secure our software supply chains. We start with a deep dive into Scorecards, an OpenSSF project that helps organizations demonstrate their security practices. What particularly caught my attention was how Atsign has used Scorecards to address customer security concerns, especially for their open-source repositories. Chris's explanation of how Scorecards evaluate everything from dependency management to CI/CD practices reveals the practical impact of these tools on everyday development. The conversation gets especially interesting when we explore SBOMs (Software Bill of Materials). Chris breaks down how these inventory lists of software components are reshaping supply chain security, driven by recent executive orders and growing security concerns. His insights into the challenges of SBOM tooling and CI/CD integration highlight the practical hurdles teams face in implementing these security measures. I was particularly intrigued by our discussion of NIST 2.0 and its implications for cybersecurity frameworks. Chris's breakdown of how governance and modern security paradigms are evolving shows just how much the security landscape has changed. We also explore Atsign's open-source SSH NoPorts project, which offers a fascinating approach to remote administration without open ports. If you're interested in software security, supply chain management, or the future of open source, you'll find plenty of practical insights here. Chris brings both deep technical knowledge and real-world experience to the discussion, making complex security concepts accessible while maintaining their technical depth. --- # The Future of Personal AI and Privacy: A Deep Dive with Kin Co-Founder Simon Westh Henriksen URL: https://vpetersson.com/podcast/s01e08/ Date: 2024-04-08 Description: I explore the intersection of AI, machine learning, and digital privacy with Simon Westh Henriksen, co-founder of Kin. We dive into data sovereignty, personal AI assistants, and how decentralization could reshape the web, all while keeping privacy at the forefront. In this episode, I'm joined by Simon Westh Henriksen, co-founder of [Kin](https://mykin.ai/) (formerly Hyphen), to explore the fascinating intersection of AI and privacy. Simon's journey from software engineer to Web3 innovator offers unique insights into how we can build AI systems that respect user privacy while remaining highly functional. We start with Simon's path to founding Kin, which took an interesting turn during the COVID-19 pandemic when he dove deep into Web3. What particularly caught my attention was his vision for a privacy-first personal AI assistant. Simon's explanation of how they handle data locally on devices while maintaining AI functionality reveals the practical challenges of balancing convenience with security. The conversation gets especially interesting when we explore the future of AI interaction. Simon shares his thoughts on AI agents communicating with each other and the potential of decentralized web nodes for data portability. His insights into managing AI hallucinations and the ethical considerations in AI development highlight the complexities of building responsible AI systems. I was particularly intrigued by our discussion of data sovereignty in an AI-centric world. Simon's approach to giving users control over their digital data while still enabling powerful AI capabilities shows how privacy and functionality don't have to be mutually exclusive. We also explore Kin's beta program and their vision for the future of personal AI assistants. If you're interested in AI, privacy, Web3, or the future of personal computing, you'll find plenty of practical insights here. Simon brings both technical depth and entrepreneurial experience to the discussion, making complex concepts around AI and privacy accessible while maintaining their technical sophistication. --- # coreboot Uncovered: BIOS Security and Vulnerabilities with Matt DeVillier and David Hendricks URL: https://vpetersson.com/podcast/s01e07/ Date: 2024-03-25 Description: I explore the world of coreboot with experts Matt DeVillier and David Hendricks, diving into how this open-source BIOS technology is reshaping firmware security. From Chromebooks to servers, we discuss the mechanics, security features, and what this means for the future of sustainable computing. In this episode, I'm joined by Matt DeVillier (Mr. Chromebox) and David Hendricks to explore the fascinating world of coreboot. Their combined experience from companies like AMD, Facebook, Google, and Amazon offers unique insights into how this open-source BIOS technology is transforming firmware development. We start with Matt's journey from hardware enthusiast to coreboot expert, and David's early work with project founder Ron Minnich. What particularly caught my attention was the contrast between coreboot and U-Boot, especially in how they're used in Chromebooks and servers. Their explanations of Secure Boot, verified boot, and UEFI Secure Boot reveal the critical role BIOS plays in system security. The conversation gets especially interesting when we dive into recent BIOS vulnerabilities like LogoFail and PixieFail. Matt and David share candid insights about supply chain security and the importance of transparency in firmware development. Their discussion of Software Bill of Materials (SBOMs) highlights how crucial firmware integrity has become in modern computing. I was particularly intrigued by our discussion about transitioning to coreboot. Their emphasis on early engagement with Original Design Manufacturers (ODMs) and the potential of RISC-V shows both the challenges and opportunities in open hardware. We also explore how coreboot contributes to sustainable computing, especially in developing countries. If you're interested in firmware security, open-source development, or sustainable computing, you'll find plenty of practical insights here. For those wanting to dive deeper, here are some valuable resources: - [coreboot's homepage](https://www.coreboot.org) - Documentation, consultant links (coreboot IBVs), and hardware vendor information - [MrChromebox.tech](https://mrchromebox.tech) - Matt's custom coreboot distribution - [Converged Security Suite](https://github.com/9elements/converged-security-suite) - Tools for Bootguard provisioning - [goswid](https://github.com/9elements/goswid) - SBOM generation tool for coreboot - [Developer Information for Chrome OS Devices](https://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices) - Comprehensive guide for ChromeOS devices using coreboot --- # The Future of 5G and 4G: A Deep Dive with Guillaume Bélanger URL: https://vpetersson.com/podcast/s01e06/ Date: 2024-03-11 Description: I explore the evolving world of telecom with Guillaume Bélanger from Canonical, diving into how open source is transforming the industry. From e-SIMs to private 5G networks, we unpack the technologies that are reshaping our connected future. In this episode, I'm joined by Guillaume Bélanger from Canonical to explore the fascinating world of telecom innovation. Guillaume's extensive experience in the sector offers unique insights into how open source is revolutionizing the industry. We start by diving into groundbreaking projects like Magma and SD-Core that are reshaping private network management. What particularly caught my attention was Guillaume's explanation of how these initiatives are transforming traditional network infrastructure. His breakdown of e-SIM technology and its potential to revolutionize device connectivity reveals just how much the landscape is changing. The conversation gets especially interesting when we explore the technical aspects of 5G. Guillaume walks through the intricacies of core and radio networks, software-defined networking, and the practical challenges of setting up private 5G networks. His insights into the hardware requirements and regulatory considerations provide a comprehensive view of what it takes to deploy these systems. I was particularly intrigued by our discussion about tech giants like Google and Amazon entering the telecom space. Guillaume's perspective on how these companies are influencing the industry, combined with Canonical's open source approach, highlights the evolving dynamics of modern telecommunications. If you're interested in network infrastructure, telecom innovation, or the future of connectivity, you'll find plenty of practical insights here. Guillaume brings both deep technical knowledge and industry experience to the discussion, making complex telecom concepts accessible while maintaining their technical depth. --- # A Global Mission to Connect: Unveiling Giga's Journey with Chris Fabian URL: https://vpetersson.com/podcast/s01e05/ Date: 2024-02-26 Description: I explore the ambitious mission of connecting every school to the internet with Chris Fabian, co-founder of Giga. From leveraging blockchain to machine learning, we dive into how technology can help bridge the digital divide and transform education globally. In this episode, I'm joined by Chris Fabian, co-founder of Giga, to explore the ambitious goal of connecting every school in the world to the internet. Chris's journey from setting up ISPs in East Africa to driving innovation at UNICEF offers unique insights into the challenges of global connectivity. We start with Giga's innovative approach to mapping schools using open-source machine learning and satellite imagery. What particularly caught my attention was how this method has revealed significant gaps in government data, with Giga having mapped an impressive 2.1 million schools so far. Chris's explanation of their data collection methods, combining government data with software probes in schools, shows the complexity of tracking real connectivity. The conversation gets especially interesting when we explore the economic and educational impact of school connectivity. Chris shares compelling research showing how increased school connectivity boosts both years of schooling and GDP per capita. His vision of schools as nodes in a decentralized network, supporting both education and financial inclusion through blockchain technology, reveals the broader potential of this initiative. I was particularly intrigued by our discussion of the technical challenges in connectivity. We explore the roles of different technologies - from fiber to radio and satellites - and the regulatory hurdles they face. Chris's critique of initiatives like Facebook's Internet.org raises important questions about the difference between true internet access and walled gardens. If you're interested in global development, education technology, or the future of connectivity, you'll find plenty of practical insights here. Chris brings both technical expertise and social impact experience to the discussion, making complex connectivity challenges accessible while maintaining their technical and social depth. --- # Exploring the C2PA Standard with Dom Guinard from Digimarc URL: https://vpetersson.com/podcast/s01e04/ Date: 2024-02-13 Description: I explore the revolutionary C2PA standard with Dom Guinard from Digimarc, diving into how this technology is reshaping digital content authenticity. From combating deepfakes to protecting creator rights, we unpack what this means for the future of online content in the age of generative AI. In this episode, I'm joined by Dom Guinard from Digimarc to explore the fascinating world of digital content standards. Dom's extensive experience in IoT and digital standards offers unique insights into how we can protect and authenticate content in an increasingly AI-driven world. We start with a deep dive into the C2PA (Coalition for Content Provenance and Authenticity) standard. What particularly caught my attention was how this technology combines metadata, watermarking, and hardware integration to establish content trustworthiness. Dom's explanation of how these elements work together reveals the complexity of ensuring digital authenticity. The conversation gets especially interesting when we explore the practical implications of C2PA. Dom breaks down how this standard helps combat deepfakes and unauthorized content use, while also empowering creators with more control over their work. His insights into the recent US executive order on AI and its connection to digital content standards highlight the growing importance of these initiatives. I was particularly intrigued by our discussion of how C2PA integrates with existing technological ecosystems. Dom's perspective on balancing innovation with content protection shows the careful consideration that goes into developing these standards. We also explore the practical tools available, including Digimarc's C2PA Chrome Extension. If you're interested in digital content, creator rights, or the future of online authenticity, you'll find plenty of practical insights here. For those wanting to dive deeper, here are some valuable resources: - [C2PA Standard website](https://c2pa.org) - Comprehensive information about the standard - [Content Authenticity Initiative](https://contentauthenticity.org) - Broader applications and impact - [Digimarc's C2PA Chrome Extension](https://chromewebstore.google.com/detail/c2pa-content-credentials/mjkaocdlpjmphfkjndocehcdhbigaafp?hl=en&pli=1) - Try the technology yourself --- # Nerding out about Nix and NixOS with Jon Seager URL: https://vpetersson.com/podcast/s01e03/ Date: 2024-01-29 Description: I explore the powerful world of Nix with Jon Seager from Canonical, diving into how this functional programming language and package manager is revolutionizing system reproducibility. From practical applications to advanced configurations, we unpack what makes Nix a game-changer for modern development. In this episode, I'm joined by Jon Seager, VP of Enterprise Engineering at Canonical, to explore the fascinating world of Nix. Jon's experience with automation tools like JuJu and charms offers unique insights into how Nix is transforming software development and system management. We start with the dual nature of Nix as both a functional programming language and a package manager. What particularly caught my attention was Nix's ability to create truly reproducible systems - a feature that sets it apart in the software development landscape. Jon's explanation of how NixOS combines the Nix package manager with its module system reveals the elegant simplicity of immutable system configuration. The conversation gets especially interesting when Jon shares his home setup. His integration of NixOS with TailScale for a zero-trust environment demonstrates the practical power of Nix's declarative approach. The way he maintains his system configuration shows how Nix can make complex setups both secure and manageable. I was particularly intrigued by the resources Jon has curated for the Nix community. For those wanting to dive deeper into the Nix ecosystem, here are some invaluable resources: - [Zero to Nix](https://zero-to-nix.com/) - The perfect starting point - [Determinate Systems' Nix Installer](https://github.com/DeterminateSystems/nix-installer) - Streamlined Nix setup - [Jon's NixOS Config](https://github.com/jnsgruk/nixos-config) - Real-world configuration example - [Wil T's Nix Guides](https://nixos.wiki/wiki/Wil_T_Nix_Guides) - Comprehensive learning resources - [Jon's Libations](https://github.com/jnsgruk/libations) - Creative Nix applications If you're interested in system configuration, reproducible builds, or the future of package management, you'll find plenty of practical insights here. Jon brings both deep technical knowledge and hands-on experience to the discussion, making complex Nix concepts accessible while maintaining their technical depth. --- # Nerding out about Prometheus and Observability with Julius Volz URL: https://vpetersson.com/podcast/s01e02/ Date: 2024-01-15 Description: I explore the world of system monitoring with Julius Volz, co-founder of Prometheus and PromLabs. From technical deep dives to practical implementations, we unpack what makes Prometheus the gold standard for metrics-based monitoring and get hands-on with PromQL demonstrations. In this episode, I'm joined by Julius Volz, co-founder of Prometheus and founder of PromLabs, to explore the fascinating world of systems monitoring and observability. Julius's journey from working on Borgmon at Google to co-creating Prometheus offers unique insights into how modern monitoring systems evolved. We start with the technical foundations of Prometheus. What particularly caught my attention was Julius's explanation of their dimensional data model and how it revolutionized metrics-based monitoring. His breakdown of common pitfalls, especially around metric design and "cardinality bombs," provides invaluable guidance for anyone implementing Prometheus. The conversation gets especially interesting when we dive into long-term data storage challenges. Julius shares practical insights about solutions like Cortex and Thanos, demonstrating how to handle large datasets effectively. His live demonstration of PromQL, showing functions like `rate`, `irate`, and `increase`, reveals the powerful querying capabilities that make Prometheus stand out. I was particularly intrigued by our discussion of future trends in observability. Julius's thoughts on eBPF integration, OpenTelemetry, and the OpenMetrics project show how the monitoring landscape continues to evolve. We also explore the simplicity of writing Prometheus exporters, highlighting how accessible the technology can be even for those with minimal coding experience. If you're interested in systems monitoring, observability, or infrastructure management, you'll find plenty of practical insights here. Julius brings both deep technical knowledge and hands-on experience to the discussion, making complex monitoring concepts accessible while maintaining their technical depth. --- # Nerding out about Security with Andrew Martin URL: https://vpetersson.com/podcast/s01e01/ Date: 2024-01-01 Description: I explore the world of Cloud Native security with Andy Martin from ControlPlane, diving into everything from penetration testing to zero trust architecture. From practical risk assessments to social engineering insights, we unpack what it takes to secure modern infrastructure. In this inaugural episode, I'm joined by Andy Martin from ControlPlane to explore the fascinating world of Cloud Native security. Andy's extensive experience in regulated industries like finance and government offers unique insights into modern security challenges. We start by revisiting our "Internet of Shit" conference talk, which sets the stage for a deeper discussion about current security concerns. What particularly caught my attention was Andy's perspective on penetration testing and its role in both digital and physical security assessments. His breakdown of social engineering attacks reveals just how sophisticated modern security threats have become. The conversation gets especially interesting when we dive into the ethics of hacking. Andy's analysis of Black Hat, White Hat, and Grey Hat approaches provides valuable context for understanding the security landscape. We also tackle the ongoing debate between on-premises and cloud security, examining the unique challenges each presents. I was particularly intrigued by our discussion of compliance and certification frameworks like SOC 2 and ISO 27001. Andy's practical threat modeling exercise demonstrates real-world risk assessment strategies that organizations can implement immediately. We also explore supply chain security and Software Bills of Materials (SBOMs), highlighting their growing importance in modern software development. If you're interested in cybersecurity, cloud infrastructure, or risk management, you'll find plenty of practical insights here. Andy brings both deep technical knowledge and real-world experience to the discussion, making complex security concepts accessible while maintaining their technical depth. --- # The Last 20% URL: https://vpetersson.com/2026/08/21/the-last-20-percent/ Date: 2026-08-21 ![A Claude prompt box reading "Hey Claude, build me a new accounting software to replace Xero. Don't make any mistakes."](/assets/claude-replace-xero.png) The other day my friend Chris pinged me on a [Twitter thread](https://x.com/vpetersson/status/2089637913709465773). [Todd Dailey](https://x.com/twid/status/2085407667556614259) was making the case that digital signage is a huge industry selling bad products with high subscription fees, and that with a $40 e-ink ESP32 board you could just go build your own conference room signage with AI. Fair point. I'd add that the security posture across the industry is pretty terrible too. But it got me thinking. Everyone is talking about how AI is going to kill every industry. Anyone with a Claude subscription is apparently going to replace every SaaS tool they pay for with something they vibe coded themselves. It's a compelling story. We all like to think we can build something better than the incumbent. Now we have the tool. Don't get me wrong. I have been [vocal about enshittification](https://www.linkedin.com/pulse/enshittification-viktor-petersson-zovhe) and how shitty much of the software world is. The more niche the vertical, the shittier the product. Historically you could get away with it, because of the moat of insights (and to some degree, resources). So gather round, kids. Let me tell you about this exact thing. One of my first ventures into the world of startups was a now decommissioned product called YippieMove. That was almost 20 years ago. The product was straightforward: it copied the contents of one email account to another. The use case was either that you were switching services (a company moving from platform X to Y), or you wanted a backup of your email. On the face of it, it seems like an easy thing to build. You are literally copying from one server to another using a standardized protocol. There is even an [RFC](https://www.rfc-editor.org/rfc/rfc9051.html) for it. Should be simple. It wasn't. And this is where armchair entrepreneurs massively underestimate the complexity of building a product for the real world. Yes, with today's tools you could probably build 80% of YippieMove in a week. That's true for most products out there. You can clone 80% of them in days by vibe coding with Claude (assuming you know what you are doing). But it's the last 20% that's hard. That's where the edge cases live. That's where the hard-won lessons are. In the case of YippieMove, Claude would have implemented the RFC and claimed it had the _perfect_ solution. On paper, it would be right. In practice, it's naive. Any seasoned engineer will tell you the same thing. Standards are great, but in the real world they are only "kind of" followed. If everyone implemented them perfectly, life would be easy. They don't. For $REASONS, companies like to put their own flavor on things. They interpret standards to suit their environment. Usually to fit their own systems. Email is a great example. Yes, there is a standard behind IMAP, but once you start moving email around at scale you run into a billion edge cases. Gmail is a good one. Labels don't exist in IMAP. They get mapped to folders, which do exist. By default every message lives in All Mail, and there is another copy for every label that message carries, including the inbox. So you need to copy each email N times, because you can't make assumptions about the destination. If the destination is also Gmail, the copies get deduplicated. If it isn't, the user ends up with N copies. That's a tame example. Here's a hairier one. Flags. They're an internal building block in IMAP, used for things like read/unread status. Sounds well defined enough. Wrong. Some servers support all flags. Others support only some. And it isn't just inconsistent across servers, it's inconsistent across versions of the same server. Microsoft Exchange was a great example of this. There are plenty of open source IMAP servers where an agent could plausibly check the behavior and build support around it, but many are proprietary. The only way to learn is trial and error. We moved tens of thousands of accounts over the years, and the edge cases kept coming. Every time we thought we had it figured out, something new showed up. That's the point. The first 80% is easy. It was already largely a solved problem with Django, Rails, React, Tailwind and the rest. The remaining 20% is what separates your vibe coded "SaaS killer" from a battle-tested product. Lovable and its peers are a layman's version of those same frameworks. They give you the boilerplate. Not the hard part. Most armchair entrepreneurs have never built anything for the real world. That's why they have no idea about the agony and the time it takes to solve the remaining 20%. It takes real data. Real users. Real time. It's painful, slow and annoying. But it's the difference between a battle-tested product (commercial **or** open source) and your yolo'd vibe coding project. Claude can help you implement the lessons from those edge cases fast and well. It just can't give you the insights. --- # proxmox-imgctl: A Single Rust Binary for Proxmox Cloud Image Templates URL: https://vpetersson.com/2026/05/14/proxmox-imgctl/ Date: 2026-05-14 A few weeks ago I [wrote up](/2026/04/23/ubuntu-cloud-images-on-proxmox/) the bash scripts I use to turn Ubuntu cloud images into Proxmox templates and clone them into VMs. The scripts work, but every time I went to spin something up I found myself re-reading the post to remember which env vars to set, which storage pool I'd picked, and which snippet file was the current "good" one. The scripts were a recipe, not a tool. So I built the tool. [`proxmox-imgctl`](https://github.com/vpetersson/proxmox-imgctl) is a single Rust binary you drop on a Proxmox node. Run it, answer a handful of menu prompts, and you get a verified cloud image template or a fresh VM clone, with cloud-init wired up, secure boot enabled, and the right disk layout. No script collection, no Python runtime, no Ansible playbook. Just a binary. It's what I've been using myself for the last few weeks. The bash scripts in the previous post are retired. ## What it does Two top-level actions: - **Build template from cloud image.** Pick a distro from a curated catalog (Ubuntu 26.04 / 24.04 / 22.04, Debian 13 / 12 / 11), pick a storage pool and a VMID, confirm. The binary downloads the image, verifies it against the upstream `SHA256SUMS` / `SHA512SUMS`, creates the VM shell with OVMF + secure boot + cloud-init drive + virtio, imports the disk, and marks it as a template. - **Spawn VM from template.** Pick a template, pick a cloud-init profile (`minimal`, `dev`, `docker`, an interactive generator, or any existing snippet on disk), answer prompts for username and `ssh_import_id`, and the binary clones, resizes, attaches the snippet, and starts the VM. Each step that mutates state shows a plan and asks for confirmation before executing. There's a `--dry-run` flag that prints every `qm` command, snippet write, and download it would do, without touching anything. Useful for previewing on a non-root account or even off the Proxmox node entirely. ## Why a single Rust binary Cluttering up `/usr/local/bin` with three bash scripts, a checksum file, and a snippets directory was the worst part of the old workflow. Cluttering it with a Python tool plus `requirements.txt` plus a virtualenv would have been worse. So: - **Single static binary.** `x86_64-linux-musl`, no glibc dependency, no runtime. The same binary runs on PVE 8 (Debian 12, glibc 2.36) and PVE 9 (Debian 13, glibc 2.41) without a rebuild. Drop it in `/usr/local/bin/proxmox-imgctl`, done. - **Rust.** Cheap to ship, fast to start, strict about errors. The downloader, the SHA256/SHA512 verifier, and the interactive prompts are all in the same binary with no shelling out except to `qm`, `pvesm`, and `pvesh`: the three Proxmox CLIs I genuinely need. - **No new abstractions over Proxmox.** The tool shells out to `qm` rather than talking to the HTTP API, because (a) it's designed to run on the node anyway and (b) `qm disk import` has no clean API equivalent. The wrapper layer is thin and easy to swap if I ever want to make it run remotely. It also gracefully handles the [`qm importdisk` -> `qm disk import` rename](https://pve.proxmox.com/wiki/Roadmap#Proxmox_VE_8.0) that landed in PVE 8: it tries the modern form first and falls back to the legacy name only if the CLI rejects the syntax. One binary, both PVE majors. ## Install A static musl binary is attached to every tagged release. On the Proxmox node, as root: ```bash curl -fsSLO https://github.com/vpetersson/proxmox-imgctl/releases/latest/download/proxmox-imgctl-x86_64-linux curl -fsSLO https://github.com/vpetersson/proxmox-imgctl/releases/latest/download/proxmox-imgctl-x86_64-linux.sha256 sha256sum -c proxmox-imgctl-x86_64-linux.sha256 install -m 0755 proxmox-imgctl-x86_64-linux /usr/local/bin/proxmox-imgctl ``` First run as root seeds `/etc/proxmox-imgctl.toml` with sensible defaults (`storage`, `snippet_storage`, `snippet_dir`, `bridge`, `cache_dir`) and exits so you can review them. ## Building a template ```text $ sudo proxmox-imgctl ? What do you want to do? Build template from cloud image ? Base image: Ubuntu 24.04 (noble) ✓ Cached image at /var/lib/proxmox-imgctl/cache/ubuntu-24.04-noble.img matches checksum. ? Storage pool: local-lvm ? Network bridge: vmbr0 ? Template VMID: 9000 ? Template name: ubuntu-noble-template ? Memory (e.g. 1024M, 2G): 1G ? CPU sockets: 1 ? Cores per socket: 1 Plan: base image: https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img -> /var/lib/proxmox-imgctl/cache/ubuntu-24.04-noble.img vmid: 9000 name: ubuntu-noble-template storage: local-lvm bridge: vmbr0 resources: 1 socket(s) × 1 core(s) = 1 vCPU, 1G memory ? Proceed? Yes -> Creating VM shell... -> Importing disk (this can take a minute)... -> Attaching disk, configuring boot, adding cloud-init drive, marking as template... ✓ Template 9000 (ubuntu-noble-template) ready. ``` Spawning a VM from that template is the same shape: pick a template, pick a profile (`minimal`, `dev`, `docker`, or your own), confirm. The `dev` profile, for what it's worth, is what I use for the agent-sandbox VMs I described in the last post: a single user, passwordless sudo, SSH keys imported from GitHub, plus `git`, `build-essential`, `curl`, `vim`, `htop`, `tmux`, `jq`. The `docker` profile adds the official `get.docker.com` installer on top. ## Image catalog | Distro | Release | Codename | | ------ | --------- | -------- | | Ubuntu | 26.04 LTS | resolute | | Ubuntu | 24.04 LTS | noble | | Ubuntu | 22.04 LTS | jammy | | Debian | 13 | trixie | | Debian | 12 | bookworm | | Debian | 11 | bullseye | Each entry resolves through the upstream `current` / `latest` symlink, so a fresh download always grabs the latest point release. ## Known limitations - **amd64 only.** No arm64 release builds yet. The code is portable; I just don't have an arm64 PVE node to validate against. - **Hardcoded catalog.** Adding a distro means appending an entry to `src/catalog.rs` and rebuilding. Good enough for now. I'd rather pin the checksum logic per distro than have a free-form config. - **Single-node mindset.** The tool doesn't yet help you target a specific node on a cluster. - **No delete / edit actions.** Build template and spawn VM are the only two top-level actions; cleanup is `qm destroy` and `qm stop` directly, the way you'd do it anyway. ## Where it goes next The bash-script version of the workflow lived in my head and in a blog post. This one lives in a binary I can hand to someone else, with a release pipeline, CI that runs `fmt --check`, `clippy -D warnings`, and a build/test pass on every PR. That makes the OpenTofu / declarative version I gestured at in the last post a much smaller jump: the catalog, the profiles, and the `qm` invocations are all already factored into modules. Wiring them up behind a Terraform provider, or just behind a non-interactive CLI mode, is the obvious next step. In the meantime: if you're managing Proxmox cloud images and the scripts in the last post felt like the right idea but the wrong ergonomics, give the binary a spin. [github.com/vpetersson/proxmox-imgctl](https://github.com/vpetersson/proxmox-imgctl) --- # Ubuntu Cloud Images on Proxmox URL: https://vpetersson.com/2026/04/23/ubuntu-cloud-images-on-proxmox/ Date: 2026-04-23 I've been clicking through the Ubuntu installer on Proxmox since 2019 -- I even [wrote a note to myself](/2019/10/03/ubuntu-core-on-proxmox/) back then about the dance. Partition the disk, pick a locale, wait for packages, reboot, SSH in, install the things I always install. Ten minutes per VM, every time. What finally pushed me to fix it was wanting ephemeral Ubuntu VMs for development -- in particular, disposable sandboxes for running AI coding agents. My first instinct was to run Multipass inside a Proxmox VM. That afternoon I fought snap confinement (the daemon couldn't see `/tmp`), tildes not expanding inside quoted arguments, and native mounts that happily clobbered the `authorized_keys` Multipass had just injected. I climbed back out of that rabbit hole and realized the answer was one layer up the stack, not two layers down: Proxmox supports cloud-init natively. Template once, clone in seconds, bootstrap at first boot. ## Cloud Images vs ISOs Ubuntu ships a [cloud image](https://cloud-images.ubuntu.com/) as a pre-installed qcow2 disk: `noble-server-cloudimg-amd64.img`. No installer. No locale picker. No partitioning. Boot it and `cloud-init` runs on first start, reading a user-data file you provide and configuring the machine -- users, SSH keys, packages, arbitrary commands. This is the exact same image AWS, GCP, Azure, and DigitalOcean hand you when you spin up an Ubuntu instance; Canonical just happens to publish it for the rest of us too. Turn that image into a Proxmox template, and every new VM is a `qm clone` away. Attach a per-VM `cicustom` snippet with your cloud-init YAML, resize the disk, start the VM. First shell prompt in under a minute. ## Building the Template Here's the script I run on the Proxmox host to build the template from scratch. It's idempotent -- destroy the old template, download a fresh image, verify the checksum, rebuild: ```bash #!/bin/bash set -euo pipefail # Configuration VMID=${VMID:-9000} STORAGE=${STORAGE:-local-lvm} BRIDGE=${BRIDGE:-vmbr0} IMAGE_URL="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img" IMAGE_FILE="noble-server-cloudimg-amd64.img" # Destroy existing template if present if qm status $VMID &>/dev/null; then echo "Destroying existing VM/template $VMID..." qm destroy $VMID --purge fi # Download and verify image echo "Downloading Ubuntu Noble cloud image..." wget -q --show-progress "$IMAGE_URL" wget -q "https://cloud-images.ubuntu.com/noble/current/SHA256SUMS" sha256sum --check --ignore-missing SHA256SUMS echo "Checksum OK" # Create VM qm create $VMID \ --name "ubuntu-2404-template" \ --ostype l26 \ --memory 1024 \ --agent 1 \ --bios ovmf --machine q35 --efidisk0 ${STORAGE}:0,pre-enrolled-keys=1,ms-cert=2023k \ --cpu host --sockets 1 --cores 1 \ --vga serial0 --serial0 socket \ --net0 virtio,bridge=${BRIDGE} # Import disk qm importdisk $VMID "$IMAGE_FILE" $STORAGE # Configure disks - imported disk lands on disk-1 because efidisk takes disk-0 qm set $VMID \ --scsihw virtio-scsi-pci \ --virtio0 ${STORAGE}:vm-${VMID}-disk-1,discard=on,iothread=1 \ --boot order=virtio0 \ --scsi1 ${STORAGE}:cloudinit # Convert to template qm template $VMID # Cleanup rm -f "$IMAGE_FILE" SHA256SUMS echo "Template $VMID ready." ``` A few of the choices worth calling out: `--agent 1` enables the QEMU guest agent (graceful shutdown, IP reporting). `--vga serial0 --serial0 socket` routes the console to serial, which means `qm terminal $VMID` gives you a working shell -- essential when cloud-init falls over and you need to see what happened. And the `SHA256SUMS` check is not optional; you're baking this image into every future VM. The image is also shim-signed, so `pre-enrolled-keys=1` on the efidisk gets you secure boot out of the box -- the same posture you'd get on AWS or GCP. Flip it to `0` if you want to enroll your own keys instead. The `ms-cert=2023k` part matters too: Microsoft's 2011 UEFI CA certs expire in June 2026, and without that option Proxmox will mint the efidisk with only the old certs, nagging you at every VM start. Setting it now means fresh clones are ready for the cert rotation without manual `qm enroll-efi-keys` calls later. ## The efidisk Gotcha The gotcha: when you add `--efidisk0` _before_ importing the cloud image, the EFI vars disk takes `disk-0` and the imported OS disk lands on `disk-1`. If you mindlessly script `--virtio0 ${STORAGE}:vm-${VMID}-disk-0` you'll "successfully" boot the EFI variable partition, watch it fail silently, and spend twenty minutes running `qm config` trying to figure out why. The script above references `disk-1` for a reason. ## Cloning and Customizing Once the template exists, spinning up a new VM is a short script: ```bash #!/bin/bash set -euo pipefail # Usage: ./launch-vm.sh [disk_size] VMID=${1:?Usage: $0 [disk_size]} NAME=${2:?Usage: $0 [disk_size]} DISK_SIZE=${3:-20G} # Accept a bare number as gigabytes [[ "$DISK_SIZE" =~ ^[0-9]+$ ]] && DISK_SIZE="${DISK_SIZE}G" TEMPLATE_ID=${TEMPLATE_ID:-9000} STORAGE=${STORAGE:-local-lvm} CORES=${CORES:-4} MEMORY=${MEMORY:-4096} CLOUDINIT_SNIPPET=${CLOUDINIT_SNIPPET:-"local:snippets/ai-worker.yaml"} # Clone template echo "Cloning template $TEMPLATE_ID -> VM $VMID ($NAME)..." qm clone $TEMPLATE_ID $VMID --name "$NAME" --full --storage $STORAGE # Configure VM qm set $VMID \ --cores $CORES \ --memory $MEMORY \ --balloon 0 \ --ipconfig0 ip=dhcp \ --cicustom "user=${CLOUDINIT_SNIPPET}" # Resize disk to absolute size (qm resize grows, never shrinks) qm resize $VMID virtio0 $DISK_SIZE # Start qm start $VMID echo "VM $VMID ($NAME) started with ${DISK_SIZE} disk." echo "Watch boot: qm terminal $VMID" ``` The interesting flag is `--cicustom`. Proxmox's built-in cloud-init panel handles the basics (SSH keys, DNS, IP, hostname) but `cicustom` lets you point at a full cloud-init YAML snippet for `users`, `packages`, `runcmd`, `write_files` -- anything cloud-init supports. Snippets live in `/var/lib/vz/snippets/` by default. One caveat if you're running a Proxmox cluster: `/var/lib/vz/snippets/` is per-node. If you want a snippet available across nodes, put it on shared storage (CephFS, NFS) and reference it through that storage's snippet path. The `qm resize` step matters too. Cloud images ship with a small root disk (2-3 GB). `cloud-init` will expand the filesystem to fill the disk on first boot, but only if the disk itself is bigger, so resize _before_ start. ## A Minimal Cloud-Init Snippet Here's a stripped-down example of what goes in the snippet file -- Docker, an `ubuntu` user with passwordless sudo, and SSH keys pulled from GitHub: ```yaml #cloud-config packages: - qemu-guest-agent - ca-certificates - curl package_update: true package_upgrade: true users: - name: ubuntu groups: [sudo] shell: /bin/bash sudo: ALL=(ALL) NOPASSWD:ALL ssh_import_id: - gh:vpetersson runcmd: - curl -fsSL https://get.docker.com | sh - usermod -aG docker ubuntu - systemctl enable --now qemu-guest-agent final_message: "Bootstrap complete." ``` The coolest bit here is `ssh_import_id: gh:vpetersson`. Cloud-init pulls your public SSH keys straight from GitHub at first boot. No more copying `authorized_keys` around, no key-wrangling scripts. My production snippet adds `uv`, `bun`, `gh`, and Claude Code for the AI-agent sandbox -- the pattern is the interesting bit; swap in whatever toolchain your VMs need. ## Why a VM Per Project The reason this workflow matters more than it used to is simple: I don't trust the sandboxing in AI coding agents. Every agent I've used has, at some point, done something I didn't ask for -- misread an instruction, skipped a confirmation, or blasted past an allow-list it was supposed to respect. Treating the agent's built-in sandbox as a real security boundary is a bet I've stopped making. So the pattern I've settled on is one VM (or LXC container on Proxmox) per project. Inside that VM I'm happy to let the agent run in whatever "dangerous" / auto-approve mode the tool offers, because the blast radius is bounded by the VM itself. If something goes sideways -- rewrites my shell config, `rm -rf`s the wrong directory, pushes junk to a remote -- it's confined to a box I can `qm destroy` and rebuild from the template in seconds. Each environment also gets its own SSH key pair -- fresh keys for git operations and commit signing, never a copy of my personal key. If an agent ever does go rogue, the git history tells me exactly which environment pushed what. The audit trail survives even if the VM doesn't. This post was drafted by Claude Code running in an LXC container set up exactly this way -- dedicated, scoped SSH keys, happy to run in dangerous mode because the blast radius stops at the container boundary. ## Multipass Still Has a Place Since this post opened with me abandoning Multipass, let me close the loop honestly: Multipass is a great tool, and I still use it on my laptop. `multipass launch --cloud-init init.yaml 24.04` is the fastest way to get a throwaway Ubuntu shell on bare-metal Linux or macOS, and the same cloud-init file works in both worlds. The other thing Multipass gets right is the ergonomics around ephemeral workflows. `multipass launch`, `multipass shell`, `multipass transfer instance:/path`, `multipass delete --purge` -- shunting files in and out of a short-lived instance is basically a one-liner in each direction. On a full Proxmox VM you're back to `scp`, SSH keys, firewall rules, and (if the VM is short-lived) orchestrating all of that around a clone/destroy cycle. It's doable, but there's real overhead. For a quick "spin up an Ubuntu sandbox, poke at it, grab the output, throw it away" loop, Multipass wins on sheer friction. The mismatch was the _context_. Running Multipass inside a Proxmox VM means nesting KVM twice, fighting snap confinement on every file transfer, and reinventing primitives Proxmox already gives you -- templates, cloning, per-VM cloud-init injection. The heuristic I landed on: if your workstation is the host, Multipass. If Proxmox is the host, use the templates. ## What's Next The 2019 version of me would be delighted. The primitives haven't really changed -- `qm importdisk`, OVMF, cloud images all existed back then -- but the workflow is night and day. The obvious next step is to push all of this into [OpenTofu](https://opentofu.org/) via the `bpg/proxmox` provider, so the template, the snippet, and the VM lifecycle are all declarative and version-controlled. That's probably the next post. --- # Leaving the Cloud (Again) URL: https://vpetersson.com/2026/04/04/leaving-the-cloud-again/ Date: 2026-04-04 Digital sovereignty is becoming an increasingly hot topic, and people are starting to wake up to just how large the premium is for "cloud" versus simply running a few dedicated servers. For me, this is full circle -- not in the technology, but in the operational model. I've been managing my own servers since I was running FreeBSD jails in the early 2000s, scaling one of my early companies to hundreds of jails across dedicated hardware at Rackspace. FreeBSD jails were among the earliest container-like technologies, and the lineage from jails to Docker to LXC containers on Proxmox is a direct one. The abstraction has gotten better, but the core idea -- isolated workloads on hardware you control -- hasn't changed. Like most of the tech world, I eventually moved to Linux entirely. I was early on Docker, then early on Kubernetes -- both Linux-first ecosystems, as were all the major cloud vendors. I stayed in that world for years. It wasn't until I started [sbomify](https://sbomify.com) that I began reassessing my cloud-by-default strategy. Yes, Google gave us some startup credits, but not nearly enough to justify getting locked into their ecosystem. Cost was a factor, but so was digital sovereignty. One of sbomify's core value propositions is providing a CRA compliance easy button. Most of the customers we're talking to are in Europe, and for them, where data is hosted matters, as does the jurisdiction of the companies holding it. We also expect many of our users to self-host sbomify, so it's important that we understand the self-hosted experience firsthand and support it well. ## The Stack Having run Proxmox at home for years, it was the natural starting point. A Proxmox cluster gives you a solid foundation for spinning up both LXC containers and VMs across N nodes, with easy workload migration between them. One thing worth calling out: Proxmox now supports ZFS for the root drive, not just data volumes. This is a significant upgrade. ZFS brings checksumming and copy-on-write to every write, which means silent data corruption gets caught rather than silently propagated. It also makes snapshots and backups first-class operations -- you can snapshot the entire system state before a risky change and roll back in seconds if something goes wrong. For hosting, I landed on Hetzner. The UI isn't exactly state of the art, but it does everything I need. I provisioned a few servers with Proxmox, set up VLANs, and had a cluster running quickly. One thing to note: installing Proxmox requires Hetzner's KVM console service, which you need to manually request from support. One constraint worth knowing: by default, IPs are allocated per host. To have IPs span nodes for ingress, you need to allocate them to a VLAN. Getting an IPv6 block assigned was straightforward. Those addresses are added as AAAA records in Cloudflare, which handles IPv6-to-IPv4 translation for anything that needs it. ## Hiccups Along the Way Getting everything up and running went smoothly after working through a couple of Hetzner-specific quirks. The firewall doesn't differentiate between regular and VLAN traffic, which tripped me up initially. Once that was sorted, I started seeing random connection errors in the logs. After some head-scratching, the culprit turned out to be MTU. Hetzner uses an MTU of 1400 (versus the standard 1500). I'd configured this on the Proxmox host but forgot to apply the same setting to Docker. Once identified, it was a straightforward fix. Since the hosts are IPv6-only, I also had to configure IPv4 on a VLAN so nodes could reach IPv4 targets. Minor, but worth noting. ## Zero Trust on the LAN The last piece was internal TLS. Sending credentials in plaintext is bad practice even on a LAN, and I wanted to get this right. I looked at using Tailscale for certificates but it felt like unnecessary complexity. Instead I went with [SmallStep](https://smallstep.com/) -- their `step-ca` server and `step` agent make certificate management surprisingly painless. You define your CA, enroll your hosts, and certificates are automatically issued and renewed. I was genuinely impressed by how little friction there was. Securing Postgres and Redis with TLS via a private CA worked well. The trickiest part was getting the clients (Django and Dramatiq) to actually use the TLS connection. Django's `DATABASES` setting accepts `sslmode` and `sslrootcert` options, which worked as expected. Dramatiq was a different story: you need to pass the Redis connection as a URL with TLS parameters (e.g. `rediss://...?ssl_ca_certs=/path/to/ca.crt`), because passing the CA settings as keyword arguments gets silently ignored. And don't forget: your application container needs access to the root CA certificate too -- either copy it into the image or volume mount it in. That's the kind of bug that passes every test but fails in production. You can see the fix in [sbomify/sbomify#856](https://github.com/sbomify/sbomify/pull/856) and the subsequent cleanup in [sbomify/sbomify#857](https://github.com/sbomify/sbomify/pull/857). ## What's Next The current setup still uses Docker Compose for the workers. The next step is to migrate those to [Talos Linux](https://www.talos.dev/) -- a minimal, immutable OS purpose-built for Kubernetes -- provisioned with [OpenTofu](https://opentofu.org/). That would give us a fully declarative, API-driven infrastructure layer while keeping everything on our own hardware. I'm also looking at adding [OpenBao](https://openbao.org/) for secret management, replacing the current approach of manually distributing secrets. You get a lot of this stuff for "free" with the major cloud vendors -- managed databases, built-in TLS, secret management, load balancing. But it isn't really free. It's more like capex vs opex: you trade upfront setup effort for an ongoing premium that only grows as you scale. Yes, there's a management cost to maintaining your own clusters. But the tooling has gotten dramatically better in recent years, while the price delta between cloud and bare metal has only increased. Not to mention free egress -- no surprise bills for the crime of serving traffic. At some point, the math stops making sense -- or the VC money runs dry. --- # Making Apple Health Data Queryable with AI URL: https://vpetersson.com/2026/02/22/making-apple-health-data-queryable-with-ai/ Date: 2026-02-22 Long before ChatGPT, I was obsessively collecting health data. Blood work going back to 2018—some in PDFs, some in spreadsheets. On the device side, I've cycled through Whoop, Withings, and others, all feeding into Apple Health. Over the years it's become the central hub for everything: heart rate, steps, workouts, sleep, body composition, blood pressure, you name it. The problem? Apple Health is great at _collecting_ data but terrible at letting you _analyze_ it. You can see today's stats, maybe a weekly trend, but asking "how has my resting heart rate changed over the past two years?" or "show me the correlation between my sleep and workout performance"—forget it. And if you want to export? You get a massive compressed archive. Mine was 1.2GB _compressed_, containing over 700 fitness activities and hundreds of blood pressure readings. The raw XML is essentially unusable. ## Existing Solutions I tried [neiltron/apple-health-mcp](https://github.com/neiltron/apple-health-mcp), but it depends on a third-party iOS app ([Simple Health Export CSV](https://apps.apple.com/us/app/simple-health-export-csv/id1535380115)) to convert the data first. That app simply couldn't handle a dataset of this size. Dead end. ## Building My Own So I built [apple-health-mcp-server](https://github.com/vpetersson/apple-health-mcp-server)—a Rust-based MCP server that works directly with Apple Health's native export. It operates in two steps: **Import**: Parses the raw XML export (plus ECG CSVs and GPX route files) and loads everything into a local DuckDB database. The Rust implementation handles multi-gigabyte exports efficiently—my 2.5GB decompressed dataset imports in under a minute on a laptop. Content-hash deduplication means re-imports are safe and only add the delta. **Serve**: Runs an MCP server exposing 12 tools for querying—listing record types, querying measurements, aggregating stats over time, browsing workouts with GPS routes, viewing ECG waveforms, and running arbitrary read-only SQL. Once running, you can ask Claude things like "compare my resting heart rate trends with my sleep data over the last 6 months" and actually get an answer. Combine that with blood work data (PDFs or spreadsheets) and you can build a comprehensive health picture that was previously impossible without a data science background. ![Health Audit generated by Claude using apple-health-mcp, combining blood work and Apple Health data](/assets/health-audit-apple-health-mcp.webp) _A health audit where Claude combines years of blood work with Apple Health data into a single analysis._ ## Details Worth Noting - Supports both Streamable HTTP (Claude Code, Cursor, Windsurf) and stdio transport (Claude Desktop) - All data stays local on your machine—nothing leaves your laptop - DuckDB means queries are fast even over years of data - Re-importing after a new Apple Health export just adds the delta ## Try It The project is open source and available on [GitHub](https://github.com/vpetersson/apple-health-mcp-server). Export your Apple Health data, point the tool at it, and start asking questions you've always wanted answers to. --- # AI is Killing (Basic) SaaS, Part 2: From Link Shorteners to Linktrees URL: https://vpetersson.com/2026/01/14/ai-is-killing-saas-part-2-linktree-link-shorteners-and-the-leverage-you-buy-back/ Date: 2026-01-14 In [Part 1](/2026/01/09/ai-is-eating-saas-building-an-ip-geolocation-api-in-two-hours), I replaced ipgeolocation.io with a self-hosted Rust service in under two hours. That was just one piece of a larger pattern we've been following at [Screenly](https://www.screenly.io). This is the story of [DSLF](https://github.com/vpetersson/dslf)—a project that started as a link shortener and evolved into a Linktree killer. ## The Link Shortener A few months ago, I got fed up paying for Rebrandly at Screenly. It's a bit.ly-style service, and while it worked fine, the monthly invoice felt silly for what amounts to a 301 redirect. So I [hacked together DSLF](/2025/07/11/dslf-a-rust-hacking-cheaper-hosting-and-two-http-codes-i-didnt-know-about). It's a tiny Rust binary that reads a CSV and spits out redirects. I even wrote an import tool for Rebrandly to make migration painless. It's been running on `go.srly.io` for Screenly and `301.vpetersson.com` for my personal stuff ever since. ## Next: Linktree With the link shortener done, I turned to another commodity SaaS: Linktree. You know these pages. They exist because Instagram only lets you add one link, so Linktree turns one link into many. Companies use them too—we do at Screenly. The thing is, these are fundamentally simple pages. A profile picture, a bio, a list of links. Yet we pay monthly for what's essentially static HTML. ### First Attempt: Failed I actually tried adding this to DSLF months ago. Cursor with the then-current model choked. The combination of multi-stage Docker builds, TypeScript for static site generation, and Rust for the server was too much for it. So I shelved the PR. ### Second Attempt: Opus 4.5 This weekend I picked up that stale PR again. With Claude Opus 4.5, I had something working within an hour. Then I added the bits that make it usable for a company: - [Catppuccin](https://catppuccin.com) theming (Mocha, Macchiato, Frappé, Latte) - Button styles (glass, solid, outline) - Social icons for all the usual suspects - Open Graph, Twitter Cards, JSON-LD for SEO - Same tiny footprint as the redirect service ## How It Works The key insight: the landing page should be fast, redirects should be flexible. The landing page compiles from YAML to static HTML at Docker build time. No template rendering at runtime—just serving files. Sub-millisecond. Redirects load from CSV at startup and can be mounted as a volume. No rebuild needed to add a new short link. Configuration is dead simple: ```yaml profile: name: "Screenly" bio: "The Secure Digital Signage Company" type: "organization" theme: preset: "mocha" buttonStyle: "glass" links: - title: "Get Started" url: "https://www.screenly.io/signup" icon: "fas fa-rocket" highlight: true - title: "Documentation" url: "https://developer.screenly.io" icon: "fas fa-book" ``` The Dockerfile uses a multi-stage build with a pre-built builder image: ```dockerfile FROM vpetersson/dslf:builder AS static COPY redirects.csv ./ COPY link-index.yaml ./ RUN bun run build FROM vpetersson/dslf:latest COPY --from=static /static/dist /app/static COPY --from=static /static/redirects.csv /app/ ``` Then just: ```bash docker build -t my-links . docker run -p 3000:3000 my-links ``` ## The Tally Three SaaS tools replaced at Screenly in the last few months: | Original SaaS | Replacement | Language | | ------------------- | ------------------------------------------------------------ | -------- | | ipgeolocation.io | [ipgeolocation](https://github.com/vpetersson/ipgeolocation) | Rust | | Bit.ly / Rebrandly | [DSLF](https://github.com/vpetersson/dslf) | Rust | | Linktree / About.me | [DSLF](https://github.com/vpetersson/dslf) | Rust | All Rust. Tiny footprint. Extremely fast. ## "Controlled" Matters I keep saying "controlled vibe coding" because the "controlled" part matters. AI-assisted code still goes through the same standards as any other change: reviewable diffs, reproducible builds, a pipeline we trust. One underrated benefit of replacing Rebrandly: everything is in git now. Full visibility, audit trails. No more "ask $name to update the link for $something." It's a PR. Maintenance? Mostly content updates and merging Dependabot PRs. These are simple tools solving simple problems. ## The Philosophy Bootstrapping isn't "spend less." It's "buy back leverage." At Screenly, we'd rather own the commodity layer than keep paying rent on it. When a service is simple enough to replicate, core enough to matter, and stable enough not to need constant innovation—it's a candidate for self-hosting. With AI-assisted development, the build-vs-buy equation has shifted. What used to be "not worth the engineering time" is now "an afternoon project." --- DSLF is Apache 2.0 licensed. Grab it from [GitHub](https://github.com/vpetersson/dslf) and see it live at [go.srly.io](https://go.srly.io) or [301.vpetersson.com](https://301.vpetersson.com). --- # AI is Eating SaaS: Building an IP Geolocation API in Two Hours URL: https://vpetersson.com/2026/01/09/ai-is-eating-saas-building-an-ip-geolocation-api-in-two-hours/ Date: 2026-01-09 We've all heard the saying that AI is eating SaaS. For some simple products, it's certainly becoming true. At [Screenly](https://www.screenly.io), we've been using [ipgeolocation.io](https://ipgeolocation.io) for a while. It's a solid service, but at its core, it's just a frontend for a GeoIP database. This morning, I saw yet another monthly invoice from them and decided to run an experiment: how far can [Cursor](https://cursor.sh) with Claude Opus 4.5 go in terms of recreating their API with feature parity? Turns out, it can do it in under two hours. In Rust. ## The Problem Many applications need to know where their users are located - for timezone detection, localization, or analytics. Services like ipgeolocation.io provide this, but they come with drawbacks: API rate limits, recurring costs, latency from external calls, and dependency on third-party uptime. ## The Solution I built a drop-in replacement that runs entirely self-hosted. It's a single binary that bundles all the data it needs - no external API calls, no API keys to manage, and responses in microseconds instead of milliseconds. **Demo**: [geoip.vpetersson.com](https://geoip.vpetersson.com) (available until it gets too much load) **Source**: [github.com/vpetersson/ipgeolocation](https://github.com/vpetersson/ipgeolocation) ## Why It's Cool (Technically) ### Zero External Dependencies at Runtime The service embeds two datasets directly: - **MaxMind GeoLite2-City** (~70MB database) for IP-to-location lookups - **tzf-rs** compiles timezone boundary polygons (~15MB) directly into the binary This means the 17MB binary contains everything needed to resolve any IP address to its location and any coordinates to their timezone - offline, airgapped, whatever. ### Rust + Axum = Blazing Fast Built on [Axum](https://github.com/tokio-rs/axum) and [Tokio](https://tokio.rs/), the async runtime handles thousands of concurrent requests efficiently. The in-memory LRU cache (via [Moka](https://github.com/moka-rs/moka)) means repeated lookups are nearly instant. ### Aggressive Caching Strategy IP geolocation data rarely changes. The service returns `Cache-Control: public, max-age=1209600` (2 weeks), making it perfect behind a CDN like Cloudflare. ### Minimal Attack Surface The Docker image uses [Chainguard](https://www.chainguard.dev/) images: - **Build stage**: `cgr.dev/chainguard/rust` (with `cargo-auditable` for SBOM) - **Runtime**: `cgr.dev/chainguard/glibc-dynamic` (minimal, CVE-free base) The final image contains just the binary, the GeoIP database, and country flag SVGs. No shell, no package manager, minimal attack surface. ### API Compatibility Two API formats are supported: - **Simple format** (`/ipgeo`, `/timezone`) - backward compatible, minimal response - **Full format** (`/v1/ipgeo`, `/v1/timezone`) - rich metadata including country info, currency, calling codes, DST details ## The Numbers | Metric | Value | | ------------- | -------------------------------------------- | | Binary size | 17MB (includes all timezone boundary data) | | Docker image | 140MB on disk, 44.5MB content | | Response time | Sub-millisecond for cached lookups | | Memory | ~100MB runtime (GeoIP DB loaded into memory) | | Startup | <1 second | Most of the disk image is the GeoIP database and country flag SVGs. The Rust binary itself is about 17MB, which includes everything. ## What This Means For simple, data-lookup SaaS products, the economics are shifting. When an AI coding assistant can recreate your entire product in a couple of hours, the value proposition needs to be more than just "we host it for you." The project is MIT licensed and available on [GitHub](https://github.com/vpetersson/ipgeolocation). The only requirement is a free [MaxMind](https://www.maxmind.com) account to download the GeoLite2 database during Docker build. --- # How I Streamlined My Jekyll Diagram Workflow with D2 and Bun URL: https://vpetersson.com/2025/11/30/redesigning-the-sbomify-site-and-falling-deeper-into-d2/ Date: 2025-11-30 Over the last few days, I gave the [sbomify website](https://sbomify.com) a much-needed overhaul. The previous iteration didn’t really reflect what we’re doing, especially as the product has matured. The new version leans heavily on diagrams to communicate concepts more clearly. For the past year, I’ve more or less standardized on [`d2`](https://d2lang.com/) for diagrams. I’ve gone through the usual journey: Lucidchart, Mermaid, UML, and a few others. They all work and have their own strengths and weaknesses, but each brought its own annoyances that made me eventually drop them. `d2`, on the other hand, instantly clicked. It’s readable, intuitive, and produces diagrams that actually look good without wrestling with the tool. It’s not perfect, but it’s the closest I’ve found to something that fits the way I think. One of the things I enjoy most is that I can quickly write out the basic diagrams due to its simple syntax and “vibe-code” them into something more elaborate. They live alongside my notes in [Obsidian](https://obsidian.md/) (which I've recently fallen in love with), where the `d2` plugin renders everything nicely. It’s a surprisingly satisfying workflow. ## The Problem: Keeping Diagrams in Sync The sbomify site itself is a fairly unorthodox Jekyll setup running on GitHub Pages. Over the last year, I’ve added more and more rendered `d2` diagrams across articles and pages. That created a maintenance headache: keeping SVGs in sync across posts is tedious. Yes, you _can_ reuse assets, but minor edits accumulate, and the overhead grows quickly. The obvious question became: _why not just use `d2` directly in the content?_ ## Experimenting With Native `d2` in Markdown My first idea was to treat `d2` like any other fenced code block, similar to how I use it in Obsidian. Simple in theory, but tricky in practice. Injecting `d2` into Jekyll’s Markdown pipeline through the `rouge` syntax highlighter, proved far more complex than I’d hoped. Instead, I took a more pragmatic path. ## The Pipeline Approach I added a build step that runs during CI (and locally) to render `d2` diagrams using [`bun run build:d2`](https://github.com/sbomify/sbomify.com/#building-diagrams), which in turn calls on [`watch-d2.ts`](https://github.com/sbomify/sbomify.com/blob/master/scripts/watch-d2.ts). While I was at it, I moved all diagram styling into a [theme](https://github.com/sbomify/sbomify.com/blob/master/_d2/theme.d2). That means: - Diagrams are significantly smaller - The style is consistent everywhere - Updating the styling automatically updates every diagram site-wide It’s a clean setup, and it removes an entire class of maintenance problems. Everything now runs as part of the CI pipeline, along with the rest of the linting and checks. The workflow feels solid, and more importantly, sustainable. --- # PostgreSQL Replication Troubleshooting: War Stories from the Field URL: https://vpetersson.com/2025/11/12/postgresql-replication-troubleshooting-war-stories/ Date: 2025-11-12 I recently wrapped up a consulting gig helping a client troubleshoot some gnarly PostgreSQL replication issues. What started as a "quick performance tune" turned into a deep dive through WAL checkpoints, replication slots, and the delicate dance of logical replication workers. Here are the war stories from the trenches, complete with the errors we hit and how we solved them. If you're dealing with PostgreSQL logical replication at scale, you might find these useful. ## The Setup The client was running a PostgreSQL logical replication setup between two servers using publication/subscription. The source server was running PostgreSQL 14, while the target server was on PostgreSQL 18. Everything was working, but performance was suffering, and the logs were filling up with concerning messages. An important constraint: we could **not restart the source server**—it was a live production system handling real traffic. This limitation would prove crucial later when we hit replication slot limits. Time to roll up the sleeves. ## War Story #1: The Checkpoint Chaos ### The Problem The first red flag was in the logs on the receiving node: ```bash [...] Nov 11 16:26:56 [redacted].internal postgres[9307]: [102-1] 2025-11-11 16:26:56 UTC [9307] LOG: checkpoints are occurring too frequently (11 seconds apart) Nov 11 16:26:56 [redacted].internal postgres[9307]: [102-2] 2025-11-11 16:26:56 UTC [9307] HINT: Consider increasing the configuration parameter "max_wal_size". Nov 11 16:26:56 [redacted].internal postgres[9307]: [103-1] 2025-11-11 16:26:56 UTC [9307] LOG: checkpoint starting: wal Nov 11 16:27:05 [redacted].internal postgres[9307]: [104-1] 2025-11-11 16:27:05 UTC [9307] LOG: checkpoint complete: wrote 1382 buffers (8.4%), wrote 0 SLRU buffers; 0 WAL file(s) added, 5 removed, 96 recycled; write=8.387 s, sync=0.943 s, total=9.559 s; sync files=19, longest=0.437 s, average=0.050 s; distance=1656483 kB, estimate=1656642 kB; lsn=D/194EB50, redo lsn=C/A2256A78 [...] ``` Checkpoints every 11 seconds? That's way too frequent. PostgreSQL was basically spending all its time doing housekeeping instead of actual work. ### The Solution The fix was straightforward — increase the `max_wal_size` parameter: ```sql ALTER SYSTEM SET max_wal_size = '8GB'; SELECT pg_reload_conf(); ``` This gave PostgreSQL more breathing room before triggering checkpoints. The frequency dropped dramatically, and performance improved immediately. But we weren't done yet. Looking at resource utilization, we noticed something interesting: CPU cores were maxed out on the receiving server, but the workload on the sending server was pretty low. When we checked `max_sync_workers_per_subscription`, it was set to just 2, explaining why we were only using two CPU cores for replication work. To squeeze out more throughput, we bumped up the sync workers on the receiving node: ```sql ALTER SYSTEM SET max_sync_workers_per_subscription = 8; SELECT pg_reload_conf(); ``` This was previously set to 2, so we quadrupled the parallelism. More workers should mean faster replication, right? Well, not so fast... ## War Story #2: The Replication Slot Shortage ### The Problem After increasing the sync workers, we started seeing a different set of errors on the **sending** node: ```bash [...] 2025-11-11 17:52:33 UTC [358108] postgres@[redacted] STATEMENT: CREATE_REPLICATION_SLOT "pg_21465_sync_18014_7571499740820591239" LOGICAL pgoutput USE_SNAPSHOT 2025-11-11 17:52:33 UTC [358109] postgres@[redacted] ERROR: replication slot "pg_21465_sync_18449_7571499740820591239" does not exist 2025-11-11 17:52:33 UTC [358109] postgres@[redacted] STATEMENT: DROP_REPLICATION_SLOT pg_21465_sync_18449_7571499740820591239 WAIT 2025-11-11 17:52:33 UTC [358109] postgres@[redacted] ERROR: all replication slots are in use 2025-11-11 17:52:33 UTC [358109] postgres@[redacted] HINT: Free one or increase max_replication_slots. 2025-11-11 17:52:33 UTC [358109] postgres@[redacted] STATEMENT: CREATE_REPLICATION_SLOT "pg_21465_sync_18449_7571499740820591239" LOGICAL pgoutput USE_SNAPSHOT [...] ``` The pattern repeated over and over. PostgreSQL was trying to create replication slots but kept hitting the limit. ### The Root Cause After some head-scratching, we figured out the math: - The sending server had **4 replication slots** total - PostgreSQL reserves **1 slot** internally - That left us with **3 available slots** - But we had configured **8 sync workers** on the receiving end Each sync worker needs its own replication slot on the sender. 8 workers, 3 slots. The math doesn't work. ### The Solution We had two options: **Option 1: Dial back the sync workers** (what we did): ```sql ALTER SYSTEM SET max_sync_workers_per_subscription = 3; SELECT pg_reload_conf(); ``` This immediately fixed the slot shortage since we now had exactly the right number of workers for available slots. **Option 2: Increase replication slots on the sender** (requires restart): ```sql ALTER SYSTEM SET max_replication_slots = 32; -- pick a number that fits your needs ALTER SYSTEM SET max_wal_senders = 32; -- keep this in step with parallelism ``` The catch? These parameters require a full PostgreSQL restart to take effect. In our case, that was off the table, but if you have the flexibility, this gives you much more headroom for scaling. ## The Lessons 1. **WAL checkpoint frequency matters**: If you're seeing frequent checkpoints, bump up `max_wal_size`. Your disks (and performance) will thank you. 2. **Replication slots are finite**: Every sync worker needs a replication slot on the sender. Do the math before cranking up parallelism. 3. **Some parameters need restarts**: `max_replication_slots` and `max_wal_senders` are restart-required parameters. Plan accordingly. 4. **Monitor your logs**: Both nodes will tell you what's wrong, but sometimes the error shows up on the opposite end from where you'd expect. ## The Takeaway PostgreSQL logical replication is powerful, but it has limits. Understanding the relationship between sync workers, replication slots, and WAL management is crucial when you're trying to scale throughput. --- # DSLF – a rust hacking, cheaper hosting, and two HTTP codes I didn’t know about URL: https://vpetersson.com/2025/07/11/dslf-a-rust-hacking-cheaper-hosting-and-two-http-codes-i-didnt-know-about/ Date: 2025-07-11 I hacked together [DSLF](https://github.com/vpetersson/dslf) today because paying a monthly fee for a plain 301 felt silly. It’s a tiny Rust service that reads a CSV and spits out redirects—nothing more. In the process I learned that HTTP has two “new” redirect codes that slipped past me years ago. ## Four ways to say “go over there” | Old code | New code | Keeps the HTTP method? | | -------- | -------- | ---------------------- | | 301 | 308 | **Yes** | | 302 | 307 | **Yes** | _301_ and _302_ can turn a POST into a GET. _307_ and _308_ guarantee the original method sticks. Neat. DSLF defaults to the classic pair, but you can switch to the modern ones with a single flag. ```bash ./dslf --modern ``` ## Quick start 1. Drop your links in `redirects.csv`: ```csv url,target,status /gh,https://github.com/vpetersson,301 /blog,https://vpetersson.com,301 ``` 2. Run the binary: ```bash ./dslf ``` It listens on `0.0.0.0:3000`. 3. Or run it in Docker: ```bash docker run -p 3000:3000 \ -v $(pwd)/redirects.csv:/redirects.csv \ vpetersson/dslf --modern ``` (`dslf` is already the entrypoint, so just pass the flag.) ## Cheap hosting on Fly.io Fly’s smallest instance plus their free credit is often enough for a personal short-link service. Point `fly.toml` at `vpetersson/dslf`, hit `fly deploy`, and you’re live for pennies—or free if your traffic is tiny. ## What I haven’t done yet - **Load testing** – numbers will come later once I point k6 at it. - **Click counts** – might add an optional flag, but only if it stays lightweight. --- Grab the code: ```bash git clone https://github.com/vpetersson/dslf ``` If you need a no-nonsense, self-hosted link shortener—and want to use those shiny 307/308 codes—give DSLF a spin and tell me what you think. --- # Codex manages my podcast URL: https://vpetersson.com/2025/07/02/codex-manages-my-podcast/ Date: 2025-07-02 When I started my podcast a year and a half ago, I looked at the tools available. For those not familiar, for many platforms (like Amazon Music and Apple Podcasts) you need to provide an RSS feed with your podcast. Now, there is no shortage of platforms that will gladly sell you this. But essentially, what you're paying for is a thin layer on top of S3 with FFmpeg and some duct tape. Long story short, I didn't want to pay for that, so I wrote and open sourced [podcast-rss-generator](https://github.com/vpetersson/podcast-rss-generator). It's a small script that takes a YAML file and turns it into an RSS feed. This can then be copied to your block storage of choice (like S3 or R2). It can even run as a GitHub Action. Over the last year I've tweaked it and made it pretty complete against the RSS standard. For the last few months, I've had Cursor populate this for me based on the transcript. That was fine, but I wanted to automate it further. Enter [OpenAI Codex](https://openai.com/blog/openai-codex). With Codex I can take this a step further. By just adding a simple `agents.md` that tells Codex how to verify the file, I can now just tell it to add a new episode. Give it the title and description. Everything else it will infer from previous episodes. It then spits out a pull request that I can just press merge on. So here's the takeaway: machine-readable and writable toolkits, like podcast-rss-generator, will become increasingly useful and important. These tools slot perfectly into the new wave of AI agents that will automate our lives. Oh and PS, this blog post was written on my phone in Notes.app, and then deployed to my website using, yep you guessed it, Codex. --- # All Roads Lead to DSLRs URL: https://vpetersson.com/2025/06/18/all-roads-lead-to-dslrs/ Date: 2025-06-18 I've been running my video podcast [Nerding out with Viktor](/podcast/) for about a year and a half now, with just under 50 episodes published. When I [started out](/2024/06/20/on-launching-a-video-podcast-in-2024/), I tried to keep things simple and cheap. I used my Logitech Brio 4K webcam - a webcam that I picked up during COVID. Not long after, I upgraded the audio by adding a dedicated microphone, the Audio-Technica ATR2100x-USB. It did the job early on, but the sound still wasn't quite crisp. Eventually, I gave in and bought the Shure MV7 (USB), which is the go-to mic for many podcasters. That one stuck and I've had no audio issues since. ![Logitech Brio 4K webcam](/assets/logitech-brio.webp) ## Attempt 1: iPhone Video was more of a headache. The Brio was okay for video calls, but the color balance was always a bit off, and Riverside [didn't support 4K recording using it](https://support.riverside.fm/hc/en-us/articles/9684909831325-Camera-Logitech-Brio-Logitech-4K-Pro). I figured I cracked it when Apple released Continuity Camera, which let me use my iPhone as a webcam. It looked great...until it didn't. Twice it dropped a bunch of frames during recordings, and once it randomly shut down (probably overheated). This was on top of the fact that my computer just refused to connect to it occasionally. After a few episodes with sub-bar quality, I gave up and started looking for alternatives. ![Continuity Camera in action](/assets/continuity-camera.webp) By now, I'd also had a bunch of guests on the show. The difference between setups is clear as day. People using DSLRs just look better. Even the best webcams can't compete to even an entry-level DSLR when it comes to sharpness and depth of field. Here are some examples of people I've interviewed who have great DSLR setups: - [The Systems Behind Managing High-Performing Remote Teams with Jon Seager](/podcast/s02e11/) - [Exploring the Depths of Linux and Open Source Innovation with Mark Shuttleworth](/podcast/s01e13/) - [Nerding out about Prometheus and Observability with Julius Volz](/podcast/s01e02/) ## Attempt 2: GoPro At this point, I thought I'd found a clever workaround: a GoPro Hero 13. On paper, it's a 4K camera packed with features, and I could use it for events too. So I bought one. That's when the trouble started. ![GoPro Hero 13](/assets/go-pro-hero-13.webp) In theory, you can connect a GoPro via USB and use it as a webcam. **In theory.** The [reviews](https://apps.apple.com/gb/app/gopro-webcam/id6477835262?) on the App Store say otherwise. I didn't realize that until after I got it. Technically speaking, I think it completely broke as webcam when Apple started to put more restrictions on webcams and GoPro simply didn't want to play ball. Apparently you can get it to work by [turning on legacy camera mode](https://discussions.apple.com/thread/255256244?sortBy=rank), but I wasn't really interested in dropping the security of macOS just because GoPro wont fix their software/firmware. But I was already committed, so I tried a workaround: buying the Media Mod (which gives you HDMI output) and hooking it up to an HDMI capture card. Technically, it worked. But the video looked terrible. It was mirrored (easy fix), but the image was blurry, distorted, and had that GoPro fisheye vibe. Not exactly what you want for a podcast. I get it, the GoPro is made for people jumping out of planes, not people sitting in front of a bookshelf. Still, for something that cost close to £500 with all the add-ons, I expected better. I returned it and went back to the drawing board. ## Attempt 3: Elgato Facecam Pro Next up was the Elgato Facecam Pro. It looked promising and had solid reviews. I placed the order. And waited. Two weeks later, it still hadn't shipped out. So I cancelled it. Maybe it's great. I'll never know. ## Attempt 4: Sony ZV-E10 Eventually, I gave in and bought a dedicated camera. I went with the Sony ZV-E10—technically a mirrorless camera rather than a DSLR. And wow. What a difference. ![Sony ZV-E10](/assets/sony-zv-e10.webp) There's no comparison. The video quality is on a completely different level. I picked it because ChatGPT recommended it (yes, really), and also because it was discounted at the time. It might be getting a bit old, but it's still a fantastic camera. It's not cheap. You'll spend more than you would on any webcam or GoPro, but you're getting a proper camera—technically mirrorless, but in the same league as a DSLR—not just a webcam. You can grab the kit with the lens for £699 directly from Sony. I opted to buy the body only and paired it with the Sigma 16mm f/1.4 DC DN Contemporary lens. That pushed the price up a bit, but I think it was worth it. What surprised me the most was how easy it was to get going. I figured I'd need an HDMI capture card, but plugging it in via USB just worked. macOS detected it right away - no drivers, no fuss. **Update:** I can't belive it took me this long to figure it out but if you have a "Mark I" (i.e. first iteration) of the ZV-E10, it will only do 720p over USB. If you want 4k, you need to use a HDMI capturing card. It took me several months to realize this, as I was convinced it was a limitation in Riverside (which I use for podcast recordings). Only when I tested this hypothesis did I realize that the limitation was on my end. ## The takeaway If you care about image quality, all roads really do lead to a DSLR – or at least a mirrorless camera. Webcams just can't compete, no matter the price. There are some decent ones out there, but they all come with compromises. Lenses are big (and heavy) for a reason. You can't fake physics in software. At least not yet. Don't get me wrong - if all you just want something that looks OK on a video call, a webcam is sufficient. But if you want to have something that really comes across as crisp and professional in 4K, you'll end up with a DSLR or a mirrorless camera. --- # From Gateway to Dongle: Lessons from My Home Assistant Overhaul URL: https://vpetersson.com/2025/06/07/home-assistant-revamp/ Date: 2025-06-07 I’ve previously written about my experiences with Home Assistant [here](/2020/05/25/homeassistant-ikea-tradfri-flux-sensors/) and [here](/2025/01/22/how-i-use-home-assistant-in-2025/). This article follows up on those posts and describes my current setup. Today, my stack includes Home Assistant (HA), Zigbee2MQTT (Z2M) and the Mosquitto MQTT server, all running in Docker with Docker Compose on a VM. Here are some things I've learned, which would might save others time and effort. --- ## The best UI is no UI I wrote this in my first HA article years ago, but it still holds true: ideally, you never touch a button or, worse, the HA app. I only reach for a light switch or HA when something unusual happens. Everything else is driven by motion sensors or time-based automations. ## Never cut power to Zigbee lights Zigbee is a mesh network. Turning off one or more nodes at the wall disrupts the mesh, and it can take a while to rebuild. Use Zigbee-based wall switches instead, especially if you live with other people. ## Use groups, not individual devices If you run a Zigbee adapter, group your lights in Z2M. This cuts network chatter and speeds up actions. ## Push actions as far down the stack as possible If something can be handled in Z2M it will be faster and more reliable than doing it in HA. Latency matters: a delay of a few hundred milliseconds on a motion sensor is the difference between a seamless experience and wondering if something is broken. Sometimes you must move logic up to HA (for example, different actions by time of day or day of week), but keep simple, binary triggers in Z2M whenever you can. ## Version-control your configuration If you’re “vibe-coding” your config, put the entire thing under `git`. Gen-AI tools love to invent syntax and spit out invalid YAML. I added two Git hooks to guard against this: 1. Lint the YAML to catch obvious errors. 2. Validate the config with HA to catch subtler ones. ```bash #!/bin/bash # Get list of staged YAML files STAGED_YAML_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.ya?ml$') if [ -n "$STAGED_YAML_FILES" ]; then echo "Running yamllint on staged YAML files..." # Run yamllint on each staged YAML file for file in $STAGED_YAML_FILES; do # Let's try to autofix first docker run -v "$(pwd):/project" ghcr.io/google/yamlfmt:latest "$file" if ! yamllint "$file"; then echo "❌ yamllint failed on $file" exit 1 fi done echo "✅ All YAML files passed yamllint" fi # Run Home Assistant configuration check docker exec home-assistant hass --script check_config -c /config # If either check fails, prevent the commit if [ $? -ne 0 ]; then echo "Pre-commit checks failed. Please fix the issues before committing." exit 1 fi ``` If both hooks pass, most easy mistakes are eliminated. I still use the UI for some automations and checks – it’s not either vibe-coding or the UI; there’s room for both. ## Choose your path wisely If you want full control and don’t mind a steep learning curve, skip gateways and use a Zigbee dongle. You’ll gain flexibility at the cost of complexity; even a simple motion-controlled light takes real effort. The easy route is a gateway such as IKEA’s. It works out of the box and is user-friendly, but locks you to one vendor. If you don’t have grandiose plans, this path serves well. I switched only because adaptive lighting kept nagging me and I wanted smart radiator controllers, but the migration was anything but painless. If you go down the HA+Z2A appaorach, here's my `docker-compose.yml` file that I use to setup the stack: ```yaml services: homeassistant: container_name: home-assistant image: homeassistant/home-assistant:stable pull_policy: always network_mode: host volumes: - /usr/local/homeassistant:/config - /etc/localtime:/etc/localtime:ro restart: always logging: driver: journald zigbee2mqtt: container_name: zigbee2mqtt image: koenkk/zigbee2mqtt pull_policy: always restart: unless-stopped ports: - "127.0.0.1:8080:8080" devices: - /dev/serial/by-id/[...]:/dev/ttyACM0 volumes: - /usr/local/zigbee2mqtt:/app/data - /run/udev:/run/udev:ro - /etc/localtime:/etc/localtime:ro environment: - TZ=Europe/London mqtt: image: eclipse-mosquitto:2 container_name: mqtt restart: unless-stopped pull_policy: always volumes: - /usr/local/mosquitto:/mosquitto - /etc/localtime:/etc/localtime:ro ports: - "127.0.0.1:1883:1883" - "127.0.0.1:9001:9001" command: "mosquitto -c /mosquitto-no-auth.conf" ``` This is then exposed using Nginx and a [Tailscale issued TLS certificate](/2022/12/23/securing-services-with-tailscale/). ## Adaptive lighting is the killer feature HA can do a lot, yet adaptive lighting is the biggest quality-of-life boost for me. Every light in my house is Zigbee, most are on motion sensors or timers, and all adjust color and brightness automatically. If you get up at 3 a.m., the bathroom lights come on in “night mode” (triggerd by an automation) – minimum brightness, warm tint. Going back to static bulbs or flipping switches dozens of times a day would be a major step backward. --- That’s my current setup and the hard-won lessons behind it. I hope they save you a few headaches on your own Home Assistant journey. --- # Sonar Is Back: A Fresh Take on BLE Device Counting URL: https://vpetersson.com/projects/bluetooth/2025/05/07/sonar-is-back/ Date: 2025-05-07 I'm excited to share that I've just given Sonar—a FastAPI-based BLE device counter I built years ago—a full overhaul and relaunched it as an open-source project on GitHub: [https://github.com/viktopia/sonar](https://github.com/viktopia/sonar) ### Why Sonar? When I first created Sonar, the goal was simple: track Bluetooth Low Energy devices nearby to estimate foot traffic in a space without specialized hardware. Over time, the codebase drifted, dependencies aged, and the project paused. Now, with modern Python tooling and container best practices, Sonar is leaner, easier to deploy, and more powerful than ever. ### What's New - **Modern service architecture** Sonar has been fully migrated from its original Django codebase to a lean FastAPI service, with the old `manage.py` and Django apps removed and a new `app/main.py` powering all BLE scanning logic. - **Improved code quality** We've adopted Ruff for linting and import sorting, and fortified the test suite with Pytest (including async tests and coverage checks). The new `requirements-test.txt` lists `pytest`, `pytest-asyncio`, `pytest-cov`, and `ruff`, ensuring consistent style and at least 80 percent coverage. - **Docker-first deployments** Sonar now ships with a production `Dockerfile` for building a container image and a simplified `docker-compose.yml` that sets up the BLE scanner with persistent storage and hardware access. - **Simplified API** A new set of REST endpoints in `app/api/endpoints.py` provides comprehensive device monitoring capabilities. The `/latest` endpoint returns current scan results and historical statistics, `/time-series` offers detailed 24-hour data with customizable intervals, and `/health` provides system status checks. All endpoints return well-structured JSON responses with robust error handling. - **Removed legacy code** The old Django analytics application, static assets (like jQuery bundles), and deprecated Balena/Raspbian scripts have been stripped away, leaving a focused, modern codebase. ### Getting Started 1. **Clone the repo** ```bash git clone https://github.com/viktopia/sonar.git cd sonar ``` 2. **Run with Docker Compose (recommended)** ```bash docker-compose up -d --build ``` 3. **Verify it's working** ```bash curl http://localhost:8000/health # Expect: { "status": "healthy", "message": "System requirements met" } ``` While Sonar is primarily designed for Raspberry Pi, it should be compatible with most other boards and devices that have Bluez-compatible Bluetooth hardware. Feel free to dive into the API endpoints, inspect the source, and contribute—issues and pull requests are very welcome. Happy scanning! --- # Saving My Wrists - A 20-Year Quest for the Perfect Keyboard Setup URL: https://vpetersson.com/2025/04/18/saving-my-wrists-a-20-year-quest-for-the-perfect-keyboard-setup/ Date: 2025-04-18 I've been using split keyboards for about 20 years now, a journey sparked by the all-too-common wrist pain that plagues many a nerd hunched over a standard keyboard. This quest for ergonomic nirvana led me down a rabbit hole of different keyboards, culminating in my current mechanical setup, and even prompted me to switch my mouse to my _left_ hand (more on that curious habit later). My first split keyboard was the Microsoft Natural Keyboard back in the late 90s. ![Microsoft Natural Keyboard](/assets/dreamhack.webp) _Me asleep at Dreamhack (circa) '98 on my MS Natural Keyboard with a Jolt Cola tower in the backdrop. This was back when Dreamhack was cool, i.e. before it was taken over by gamers._ When I started college (go [Broncos](https://www.scu.edu/)!) and moved to the US, I left my split keyboard at home in favor of a shiny new PowerBook G4 (replacing my trusty old ThinkPad T20 running [Gentoo](https://www.gentoo.org/)). Not long after, my wrists started acting up from typing too much on the built-in keyboard. That eventually led to a [ganglion cyst](https://www.nhs.uk/conditions/ganglion/). After having that treated a few times, I figured it was time to revisit the keyboard situation. What I really needed was a portable, split keyboard that could fit in my bag alongside my laptop. It had to be small and light. I ended up getting a Goldtouch Go. ![Goldtouch Go Keyboard](/assets/goldtouch-go.webp) _The portable Goldtouch Go keyboard._ This keyboard served me well. It was small and portable. But when I wrapped up college and started working more from a regular desk (instead of hopping between coffee shops and class), it was time to upgrade. I wanted something heavier that could also tilt upwards to reduce strain on my wrists. I ended up getting another Goldtouch keyboard — this time the big brother of the Go, with the incredibly catchy name SKR-4200U Mac. It was a step up and lasted me another few years. Unfortunately, the lever for locking the keyboard into position eventually snapped off, which meant the keyboard would move slightly when typing. This mechanical failure, combined with the ever-present desire for better ergonomics, pushed me towards the next evolution. ![Goldtouch](/assets/goldtouch.webp) _My beaten up Goldtouch keyboard with the leaver broken._ ## Enter mechanical keyboards Every nerd worth their salt has at some point a) toyed with switching to Dvorak and b) gone down the deep rabbit hole of mechanical keyboards and switches. I've never seriously tried a), but I've definitely spent more time than I'd like to admit on b). There's something magical about typing on a mechanical keyboard. Yes, it's the clicky sound that will probably drive your coworkers insane (yay for [remote work](/remote-work/)!), but it's also the tactile feedback. So now we've narrowed the keyboard selection to split keyboards that are also mechanical. Thankfully, that Venn diagram has a decent overlap once you're deep into keyboard nerd land. What I ended up with was a Kinesis Freestyle Pro with Cherry MX Brown switches. I went all-in and got the extras (wrist padding and the tilt kit) to optimize the setup. I've had this keyboard for about five years now, and I really rate it. It's solid. But recently, the itch to try something new came back after a few folks around me got new keyboards. ![Kinesis Freestyle Pro](/assets/freestyle_pro.webp) _This is my current setup. Kinesis Frestyle Pro with a Magic Trackpad on the left (see below)._ I've heard great things about Bastard Keyboard's [Charybdis MK2](https://bastardkb.com/product/charybdis-mk2-prebuilt-preorder/) and splitkb's [Halcyon Corne](https://splitkb.com/collections/keyboard-kits/products/halcyon-corne). Then there's ZSA's [Moonlander](https://www.zsa.io/moonlander) and a bunch of other DIY kits. If you keep going down the keyboard rabbit hole, many roads lead to soldering your own keyboard or going ultra-minimal and using "layers" instead of having a full set of physical keys. As much as I geek out over that stuff, that's probably where I draw the line — even if it is objectively more efficient. In summary, here's the gear I've used over the years: - Microsoft Natural Keyboard (from the late 90s) - A split Logitech keyboard (from the early 00s) - Goldtouch Go - Goldtouch SKR-4200U Mac - Kinesis Freestyle Pro But the keyboard is only half the ergonomic equation. What about the other crucial input device? ## What about the mouse? One of the cool things about keyboards like the Charybdis is that it includes a built-in mouse (technically a trackball), which is great for reducing wrist movement. That setup is still pretty rare though. Generally, the best thing for ergonomics is to navigate using just the keyboard. I use `tmux` and `vim` a lot, and they rely purely on keyboard shortcuts. Tiling window managers like i3 (or [Aerospace](https://github.com/nikitabobko/AeroSpace) for macOS) help with that too. Still, chances are you'll end up using a mouse at some point. I've used various versions of the Magic Trackpad for years, mainly because of the smooth scrolling and gesture support in macOS. ## Mousing with the Left Hand ### An Ergonomic Experiment (That Stuck) Despite being right-handed, I made the switch to using my mouse left-handed about a decade ago. The motivation came from reading about how practicing skilled movements with your non-dominant hand can rewire motor networks. While it took some getting used to, the habit eventually stuck. For example: - Six weeks of left-hand mouse training in right-handers improved speed and accuracy in both hands ([Schweiger 2021](https://www.nature.com/articles/s41598-021-83770-4)). - Ten days of left-hand drawing strengthened links between cortical hand areas and the praxis network ([Philip & Frey 2016](https://pubmed.ncbi.nlm.nih.gov/27212059/)). - Six weeks of chopstick training with the left hand shifted control from effort-heavy prefrontal regions to more efficient premotor circuits ([Sawamura 2021](https://pmc.ncbi.nlm.nih.gov/articles/PMC8536892/)). - fMRI studies show that off-hand training reorganizes a distributed motor network ([Jung 2019](https://pubmed.ncbi.nlm.nih.gov/30741701/)). Neuroscience folks are quick to point out that this kind of neuroplasticity sharpens specific skills but doesn't magically increase IQ or memory ([BrainFacts 2019](https://www.brainfacts.org/thinking-sensing-and-behaving/thinking-and-awareness/2019/does-using-your-non-dominant-hand-make-you-smarter-080919)). I can't say if the switch helped me beyond having smoother left-hand control, but the habit stuck. ## So what keyboard am I switching to? Honestly, I'm still undecided. My ergonomic quest continues, and what I _think_ I want to try next is a dropped keyboard. I'm currently eyeing the Kinesis [Advantage 360 Signature](https://kinesis-ergo.com/shop/advantage360-signature/) with blank keycaps and will probably provide an update shortly. --- # A Decade and a Half of Remote Work URL: https://vpetersson.com/remote-work/2025/04/07/a-decade-and-a-half-of-remote-work/ Date: 2025-04-07 When I wrote [A Decade of Remote Work](/2019/05/18/a-decade-of-remote/) back in 2019, I had no idea a global pandemic would soon thrust remote work into the spotlight, turning it from niche to necessity overnight. Now, as I mark 15 years of building and running remote teams, I find myself revisiting and refining many of the lessons from the past and adding new insights gathered from recent experiences. This article is for tech startup founders and remote leaders, sharing practical advice for navigating the challenges and opportunities of long-term remote work. In the following sections, I'll explore how remote work has evolved, discuss the essential principles that remain constant, and share practical strategies for building and maintaining effective remote teams. From hiring practices to documentation systems, from team cohesion to personal boundaries, these insights are drawn from real-world experience managing distributed teams across multiple time zones and cultures. ## Remote Work Still Isn't for Everyone A core belief from my original article still stands true: remote work isn't suited to everyone. It demands discipline, strong self-management skills, and the ability to thrive without constant supervision. Over the past five years, I've encountered hires who mistakenly thought remote work meant they could take care of their toddler _while_ working, to save on childcare costs. Remote work isn't different from office-based work regarding expectations. Yes, I encourage flexible schedules (feel free to handle errands midday to beat the crowds), but when you're at your desk, you're expected to perform effectively and reliably (without distractions!). Discipline and clear boundaries are non-negotiable. It's not an excuse to do other things or hang out on the sofa watching Netflix all day. During COVID lockdowns, many young professionals who moved to large cities were suddenly confined in small apartments or stuck with roommates they barely tolerated. These circumstances highlighted that the environment matters significantly in remote productivity and mental health. It's a stark reminder that successful remote work needs an appropriate (and dedicated) workspace, something often overlooked. When hiring remote workers, ensure they have the resources to invest in a proper workspace. We give our staff a budget to set up their home office. If bringing people into an office, you wouldn't have them work on the floor: you'd give them an appropriate desk and setup. Remote is no different. But even the best equipment in the world can't solve for the lack of self-discipline. ## Career Development and Growth The transition from basic remote work principles to career development is natural, as the environment in which people work significantly impacts their professional growth. Some people argue that remote work is great for senior staff, but hinders junior team members' abilities to grow in their careers. One could certainly argue that a lot of knowledge transfer happens by osmosis (or by the watercooler) in an in-office setup. Many things are not written down and information just lives in someone's head. When you want to know, you walk over to Bob's desk and ask him how `$RANDOM_PROCESS` works. If you have built a good relationship with Bob, he will probably tell you all about it. But if you haven't, he'll probably shoo you away saying that he's too busy. Now, if we compare that to a remote organization where there is well-maintained documentation and carefully crafted SOPs (see below), you are not at the mercy of your relationship to Bob to learn about `$RANDOM_PROCESS`. You just pull up the document and glance over it. Thus I would argue that when it comes to knowledge transfer, in many ways remote is actually _better_ than in-office. Assuming it's done right. Now knowledge transfer and career development/growth are perhaps not exactly the same, but they are very much related. If you can read the SOPs and understand how and why decisions were made (see ADRs below), that can help you both understand the thinking and current processes of people further up in the ranks. When it comes to actual career development and growth, we're living in an interesting time. At no point previously in history has information been as accessible as it is today. For anything you'd like to learn about (professionally or personally), you have a wealth of fantastic content on YouTube, or you can use ChatGPT (or the likes of) as your own personal tutor. No question is too small or dumb. Thus traditional career development and growth is kind of moot. It's not about sending people off to pay for silly courses and get a piece of paper that you did it. It's about actually _learning_ something. When we do performance reviews, we of course cover career development. If a team member wants to learn something, we make a note of it and then devise a plan (largely based on publicly available content) on how to achieve that. You want to learn more about project management and planning to become a squad leader? Great, there are thousands (or probably hundreds of thousands) of hours of content readily available on YouTube for this. There is no longer _one_ way to learn and develop. Moreover, with well-crafted SOPs and ADRs (see below), you can integrate them with an LLM such that anyone in the company can ask questions and learn both how and why things are done the way they are. ## Increased regulation As remote work has become more mainstream, the regulatory landscape has evolved significantly. Since the initial article, a lot of things have changed on the legal side of running a remote-first company that spans numerous countries. With the rise of legislation like IR35 in the UK and AB5 in California, it's increasingly hard to hire staff as contractors in many regions. In response to this, we've seen a large wave of Employer of Record (EoR) companies, like Remote.com and Deel to address this. Given that we have headcount in both UK and California, as a remote-only company hiring in any of these regions, you're either forced to open a subsidiary in this country, or go through an EoR company. Both of which add a substantial premium. ## Remote-First vs. Hybrid: Choose Wisely Post-pandemic, many organizations rushed back to hybrid or fully office-based setups. I'm still firmly convinced that a fully remote model is superior to hybrid. Hybrid models create unnecessary complexity, with neither remote nor in-office employees fully satisfied. A remote-first approach demands intentionality, especially around documentation and asynchronous communication. It compels teams to write decisions and processes down clearly, which avoids confusion and ambiguity. After years of experimenting, I've found that intentional, remote-first workflows foster greater clarity and fairness across the entire organization. ## Documentation: Make It a Living Resource Documentation is the backbone of successful remote teams. It ensures transparency and preserves institutional knowledge. However, writing things down isn't enough. Documentation must be actively maintained and integrated into daily workflows. Otherwise, it quickly becomes outdated and irrelevant. We've integrated note-taking bots into nearly every meeting. These automatic notes are then reviewed, summarized, and stored within our documentation system, or referenced when disputes arise. Our primary tools include Google Docs, which has improved dramatically with pageless documents, and Phabricator (now Phorge) for wikis. The tools matter less than ensuring documentation is a living, constantly evolving resource. ## Standard Operating Procedures (SOPs): Clarity and Consistency As the team grows, SOPs become critical. They detail exactly how key tasks, from onboarding new hires to software releases, are performed. However, SOPs only add value when people actually use and update them regularly. Assigning clear ownership for updates and regular reviews ensures they don't become outdated or ignored. SOPs also address a different challenge: the bus factor. By having well-defined SOPs for how your organization does things, you are less at the mercy of a single stakeholder deciding to go AWOL, quit, or just go on vacation. Currently, we're making significant investments at Screenly to develop a Company Manual that holds SOPs for as many processes as possible. ## Architecture Decision Records: Capturing "Why" We've adopted [Architecture Decision Records](https://adr.github.io/) (ADRs), stored in a dedicated GitHub repository. ADRs document significant technical decisions and the context, along with the reasoning behind them. This approach provides a clear record, enabling new team members to understand past choices without rehashing old debates. ADRs are concise and easy to maintain, serving as a crucial knowledge base for long-term technical clarity. ## In-Person Summits: Essential for Team Cohesion Despite being remote-first, regular in-person meetups remain invaluable. Annual summits lasting five to seven days have proven ideal for us, providing enough time for meaningful work and team-building without being overly rushed. Why 5-7 days? Well, the first and the last day are write-offs. On the first day, people will be landing at different times, and you're just settling into the accommodation. Other people might even be jetlagged. On the last day, people will be departing at different times, and if you have enough people, there will be a steady stream of people dropping off throughout the day. Thus if you do three days, you only really have one day of working/hanging out together (and some might even be jetlagged). As far as locations, we've done a number of countries by now. One of the biggest deciding factors (beyond good weather) has always been visa accessibility, but your mileage may vary depending on where you have team members. Also please note that these summits aren't just meetings; they're a chance to deepen connections that strengthen remote collaboration throughout the year. ## Health and Well-Being: Promoting a Balanced Lifestyle Promoting physical health is crucial for remote workers. We've set up a Strava group integrated with Slack, creating friendly competitions and encouraging team members to stay active. It's a simple, engaging way to support fitness and healthy habits. Exercise isn't just a productivity booster, it's a mental health necessity, especially when working remotely. ## Remote Hiring in the Age of AI The challenges of remote hiring have evolved significantly with the rise of AI tools. Hiring remotely has become significantly more challenging today. Back when I wrote the initial article, you could relatively quickly place candidates in either a reject or interview bucket. However, today the candidates that previously could barely string a sentence together or answer even basic screening questions will use ChatGPT (or the likes of) to ace these questions. This makes it incredibly difficult (and time-consuming) to weed out qualified vs. unqualified candidates. Now, we're hardly the first ones to call this out, but when you're a remote-only company your pool of 'spray and pray' candidates is an order of magnitude higher than an in-office interview due to the sheer volume of possible candidates. We've since tweaked our hiring methods a fair bit, but I'd lie if I said it is a solved problem today. ## Saving Time and Setting Boundaries Remote work's biggest ongoing advantage remains the elimination of commuting, freeing significant time each day so that you can spend more time with your family (or for your hobbies or whatever). However, the saved time can disappear without proper boundaries. Maintaining consistent routines and setting firm limits on working hours protects against burnout. Personal routines and rituals help signal the start and end of the workday, maintaining productivity and balance. ## Looking Ahead After fifteen years, remote work continues to evolve, but foundational principles remain constant: hire disciplined, trustworthy individuals, document extensively, foster healthy routines, and intentionally build culture. Embracing asynchronous work, investing in mentorship and onboarding, and clearly communicating remain crucial to sustained remote success. In conclusion, remote work isn't easy, but it offers unmatched flexibility and freedom when done correctly. It requires continuous refinement, thoughtful adaptation, and clear, intentional practices. As remote work matures further, I'm excited to see how teams worldwide continue to adapt and thrive. And for the love of god, don't force your team to use Microsoft Teams. --- # Migrating Git repositories from Phabricator or Phorge to GitHub URL: https://vpetersson.com/2025/04/02/migrating-git-repositories-from-phabricator-to-github/ Date: 2025-04-02 I recently helped a team migrate their Git repositories from Phabricator to GitHub. While Phabricator was a great tool that combined code reviews, task management, and repository hosting, its official deprecation in 2021 has left many teams looking for alternatives. While moving directly from Phabricator to Phorge is often the simplest path (here's a [migration guide](https://we.phorge.it/w/why_migrating_from_phabricator_to_phorge/)), some teams prefer to keep their project management in Phabricator/Phorge while leveraging GitHub's CI/CD capabilities. Here's how we handled the Git migration: ### The Migration Process #### 1. Setting up the GitHub Repository Start by creating a private, bare repository on GitHub for each repo you want to migrate. #### 2. Creating a Dedicated Mirroring User For security reasons, we created a dedicated GitHub user (we called it `foobar-bot`) specifically for repository mirroring. This is better than using personal accounts because: - It keeps the mirroring credentials separate from personal accounts - It makes it easier to manage permissions - It provides better audit trails The setup involves: - Creating a new SSH key pair - Adding the public key to the bot's GitHub account - Storing the private key in Phabricator's credential store #### 3. Setting up Repository Access After creating the bot account, invite it to your private repository with read-only permissions. Important: Make sure to accept the invitation from the bot account itself, or the mirroring won't work. #### 4. The Actual Migration Here's the command sequence we used to migrate the repositories: ```bash git clone --mirror ssh://git@phabricator.mydomain.com/path/to/repo cd repo.git git push --mirror git@github.com:MyTeam/repo.git ``` This approach preserves all history, branches, and tags. #### 5. Configuring Phabricator/Phorge After the migration, you'll need to update Phabricator/Phorge to observe the GitHub repository: 1. Go to your repository in Phabricator/Phorge 2. Navigate to `Manage` -> `URI` 3. Update the original URI: - Set `I/O Type` to **Read Only** - Set `Display Type` to **Hidden** 4. Add a new URI: - Set `I/O Type` to **Observe** - Set `Display Type` to **Visible** - Choose **GitHub** under "Credentials" and link it to your bot account This configuration keeps everything in sync while maintaining proper access controls. ### Edge Cases One constraint with GitHub that you _may_ run into is the file size limit. Due to the way `git` is designed, you can't just _delete_ files in the git history without impacting various hashes. More on this can be found [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository). #### Soft limits Files over 50MB will yield the following error: ```bash remote: warning: File [redacted] is 71.46 MB; this is larger than GitHub's recommended maximum file size of 50.00 MB ``` You can still push these files, but it's recommended that you avoid this. #### Hard limits This is where it gets tricky. Files exceeding 100MB will outright be rejected with an error like this: ```bash remote: error: Trace: [redacted] remote: error: See https://gh.io/lfs for more information. remote: error: File [redacted] is 260.31 MB; this exceeds GitHub's file size limit of 100.00 MB remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com. To github.com:[redacted].git ``` Now, you need to be careful here and read over the GitHub page linked above. It is _possible_ to delete files from the git history, but not without consequences. The most important consequence is that you will be impacting hashes. So for instance, if you've used a hash in your CI/CD pipeline, this will **change** when you take the steps below. #### Removing files Once you've understood the consequences, let's go ahead with the solution. First, install [`git-filter-repo`](https://github.com/newren/git-filter-repo). This is a tool that can help rewrite the git history (and is recommended by GitHub). With this tool installed, you then need to run `git filter-repo --strip-blobs-bigger-than 100M` on the mirror you cloned. You can also adjust this to `50M` to be safe, but I would opt for minimum changes. After you've run that command, `git-filter-repo` will create a handy map for you in `filter-repo/commit-map`, which shows the mapping between the old and new hashes. Finally, you can now push this repo to GitHub using the command we used previously. ### Why This Matters Phabricator started as a Facebook project and gained popularity for its comprehensive feature set. While Phorge keeps the ecosystem alive, many teams are moving their Git operations to GitHub for better integration with modern development tools. This hybrid approach lets you keep what works in Phabricator/Phorge while getting GitHub's benefits. It's not about following trends—it's about making your development workflow more efficient and maintainable. If you're still using Phabricator or Phorge, now might be a good time to consider this migration. The longer you wait, the more complex it might become. --- # Troubleshooting UniFi Camera Adoption on Old G3 Pro Cameras URL: https://vpetersson.com/2025/03/01/troubleshooting-unifi-camera-adoption/ Date: 2025-03-01 I recently got a few affordable G3 Pro cameras to replace some less-than-stellar G4 Dome units I had in my setup. Once I hooked these cameras up to the network, they appeared in my UniFi Console (Dream Machine Pro) without issue. However, after clicking the "Adopt" button, they became stuck in "Restoring" mode for quite some time. Eventually, I decided to look for solutions online and found [a blog post](https://www.kilala.nl/index.php?id=2614) detailing a similar problem. My own fix turned out to be simpler, and here is how I resolved the issue: 1. **Download the Next Latest Firmware:** - Navigate to the [UniFi downloads page](https://www.ui.com/download/software/uvc-g3-pro) and locate the firmware just below the absolute latest release (for example, version 4.30.0). - If you try to use the newest release (like version 4.51.4), the camera may not recognize it. 2. **Log In to the Camera Directly:** - From your UniFi Console, note the camera's IP address. - Enter that IP address in your browser. - Use the default credentials (ubnt/ubnt) to log in. (In the blog post I read, the credentials were provided by Protect, but that was not the case here.) 3. **Upgrade the Firmware and Wait for Further Updates:** - Upload the firmware you downloaded in Step 1. - Once the camera finishes updating, it should be automatically adopted by UniFi Protect, which will then trigger subsequent upgrades as needed. By following these steps, I managed to get the cameras up and running without a hitch. If you find your G3 Pro cameras are stuck in "Restoring" mode, this process might save you a lot of time and headaches. --- # Yocto, RockPi and SBOMs: Building Modern Embedded Linux Images URL: https://vpetersson.com/2025/02/21/yocto-rockpi-and-sboms/ Date: 2025-02-21 **TLDR**: _I wanted to generate an up-to-date disk image for a Rock Pi 4 using Yocto that included CUPS and Docker to both get a better understanding of Yocto and test the new SBOM generation feature._ As with many single-board computers (SBCs) from China, the issue often isn't the board itself but rather the software. RockPi from Radxa is no exception. If you go and download the [latest disk images](https://wiki.radxa.com/Rock4/downloads) for this board, you will notice that they are all end-of-life (EoL). However, these boards are still great and work very well for many applications. This should be top of mind if you are building a product that uses any of these devices. I wanted to use one of the RockPi 4 boards I had for a simple print server. It's not a customer product, of course, but let's assume it was. Since it has the option to add eMMC storage, I find it more reliable than Raspberry Pi (I know the Raspberry Pi 5 allows for proper storage). However, given that I neither trust the Radxa disk images nor did I want to set things up on an already EoL Linux distribution, I started doing some digging. As it turns out, the RockPi is supported in Yocto. Say what you want about Raspberry Pi, but you can still download an up-to-date OS that runs on the Pi 1. In this article, I will show you not only how to build a disk image with Yocto (in this case for the Rock Pi 4, but it can easily be adjusted for other boards), but we will also talk a bit about how Yocto generates SBOMs (hint: it's really clever) and where to find your SBOMs. ## What is Yocto anyways? The Yocto Project is an open-source framework for building custom Linux distributions tailored to embedded systems. It provides a flexible, modular build system based on BitBake and OpenEmbedded, enabling developers to create highly optimized and reproducible Linux images for specific hardware. Yocto is widely used in industries like automotive, IoT, and networking due to its ability to support diverse architectures and long-term maintenance needs. With its layered architecture, extensive BSP support, and strong focus on customization, Yocto is a powerful tool for developers looking to build and maintain embedded Linux systems efficiently. I've toyed with it a few times over the years to build images for Raspberry Pis, but never really used it seriously. However, I recently crossed paths with some of the Yocto people in a CISA working group I'm co-chairing on [SBOM generation](https://github.com/CISA-SBOM-Community/SBOM-Generation). As it turns out, Yocto is very sophisticated when it comes to generating SBOMs, so I wanted to get some more up-to-date exposure to Yocto. Color me impressed. Not only did Yocto produce a Software Bill of Materials (SBOM) for me -- it did so without even asking me. Since Yocto builds everything from source and is essentially a package manager, it is able to capture all the dependencies into an SBOM. Moreover, since Yocto maintains detailed information about every dependency, it is able to generate very high-quality SBOMs. ## Key Yocto Terminology Before we dive in, here are some key terms in Yocto that you probably want to understand: - **Poky** – The reference distribution of the Yocto Project, containing the OpenEmbedded build system, BitBake, and a set of metadata - **Scarthgap** – The codename for the Yocto Project 5.0 release - **Mickledore** – The codename for Yocto 4.2 - **Kirkstone** – The codename for Yocto 4.0, a long-term support (LTS) release - **Dunfell** – The codename for Yocto 3.1, another LTS release - **Layers** – Modular additions to the base Yocto version that provide extra functionality - **BitBake** – The build tool used by Yocto to process recipes and generate images - **OpenEmbedded (OE)** – The build framework Yocto is based on - **Recipes** (.bb files) – Build instructions for individual packages or applications - **BSP** (Board Support Package) – A set of metadata and configurations for specific hardware platforms ## Building a disk image with Yocto Before we build, you will need a pretty beefy server to build this image (or a lot of time). I'm using my [home server](/2024/05/04/home-server-journey/), and I think it took about an hour or two to build the initial version. Subsequent builds will be a lot faster due to cache. I've used an Ubuntu 24.04 VM to build my disk images, and you can find the base dependencies you need to install [here](https://docs.yoctoproject.org/ref-manual/system-requirements.html). ### Let's get our hands dirty First, clone the repositories and set up the layers: ```bash $ git clone -b scarthgap https://git.yoctoproject.org/poky $ cd poky ``` ```bash # Add layers $ git clone -b scarthgap git://git.yoctoproject.org/meta-arm $ git clone -b scarthgap git://git.yoctoproject.org/meta-rockchip $ git clone -b scarthgap git://git.openembedded.org/meta-openembedded $ git clone -b scarthgap git://git.yoctoproject.org/meta-virtualization ``` ```bash $ source oe-init-build-env ``` ```bash $ bitbake-layers add-layer ../meta-arm/meta-arm-toolchain $ bitbake-layers add-layer ../meta-arm/meta-arm $ bitbake-layers add-layer ../meta-rockchip $ bitbake-layers add-layer ../meta-openembedded/meta-oe # Add docker support $ bitbake-layers add-layer ../meta-openembedded/meta-python $ bitbake-layers add-layer ../meta-openembedded/meta-networking $ bitbake-layers add-layer ../meta-openembedded/meta-filesystems $ bitbake-layers add-layer ../meta-virtualization ``` Next, adjust your `conf/local.conf` by appending these configurations: ```bash MACHINE = "rock-pi-4b" INIT_MANAGER = "systemd" DISTRO_FEATURES:append = " virtualization wifi" DISTRO_FEATURES:remove = " x11 wayland" CORE_IMAGE_EXTRA_INSTALL += "openssh cups cups-filters ghostscript qpdf vim docker e2fsprogs-resize2fs" ``` Finally, build the image: ```bash $ bitbake core-image-base ``` Note, if you're building on Ubuntu 24.04, you might need to run: ```bash $ sudo apparmor_parser -R /etc/apparmor.d/unprivileged_userns ``` After the build completes, you can find your image here: ```bash $ ls -lah tmp/deploy/images/rock-pi-4b/core-image-base-rock-pi-4b.rootfs-*.wic ``` Flash this disk image and you should be good to go. Once it's up and running, you should be able to SSH into the device using `root` and a blank password. ## On updating It's important to note that Yocto generates a disk image. By default, you cannot update this disk image by any other means than reflashing it (e.g., you can't run "apt update"). There are over-the-air (OTA) platforms that can be integrated into Yocto, such as [Mender](https://mender.io/) and [RAUC](https://rauc.io/), but by default, you need to rebuild the image from scratch to update dependencies and patch vulnerabilities. ## Finding Your SBOMs One of the cool features of Yocto is that it automatically generates SBOMs. You can find them in the deploy directory: ```bash $ ls -lah tmp/deploy/images/rock-pi-4b/*spdx* [..] ``` You can extract the SPDX file with: ```bash $ tar --zstd -xvf \ path/to/tmp/deploy/images/rock-pi-4b/core-image-base-rock-pi-4b.rootfs-*.spdx.tar.zst ``` Do note that this will generate a lot of files. You will find a file called `index.json` in there, which links to all other SBOMs using document linking. (Check out my article [Mastering SBOM Generation with Yocto](https://sbomify.com/2025/02/21/mastering-sbom-generation-with-yocto/) for more details on the SBOMs.) ## On running in production If you are intending to run this in production, please do not just copy the above. These images are configured for lab or test mode. Yocto is very well suited for production images, but you need to harden them and also have an OTA strategy in place. Alternatively, I can recommend [Balena](https://www.balena.io/), which uses Yocto under the hood and also supports the Rock Pi. ## Future improvements One limitation of the current disk image for Rock Pi is that you don't have a functional TTY. You can SSH in, or you could use a serial console, but the regular TTY doesn't work and I haven't spent much time trying to figure out why. Also, the disk system doesn't automatically expand to use all available space on the eMMC/SD. Some things I'm planning to add in the future: - Add support for Tailscale (there's a [meta-tailscale](https://github.com/ChristophHandschuh/meta-tailscale) layer) - Add support for auto disk expansion - Add WiFi support ## Resources - [Yocto Project Documentation](https://docs.yoctoproject.org/) - [Adding Docker to Yocto Project](https://kacperstapor.com/blog/24-11-2024/adding-docker-to-yocto-project) - [RAUC on Rockchip](https://www.konsulko.com/rauc-on-rockchip) --- # How I Use Home Assistant in 2025 URL: https://vpetersson.com/2025/01/22/how-i-use-home-assistant-in-2025/ Date: 2025-01-22 I've been using Home Assistant for about seven years now, starting back when I was living in a small apartment. At the time, my setup was modest: I used the **IKEA Smart Hub** (when it first launched) to tie together all my apartment's lights. As I got more comfortable with automations, I also began building [custom hardware like temperature and humidity sensors](/2019/11/16/home-assistant-and-esphome/). However, once I started adding more complexity (more devices, more automations), I realized that running Home Assistant on a Raspberry Pi just wasn't viable anymore. This was before Home Assistant offered their own hardware (which I haven't tried, so I can't say much about it). But for me, the main issue was the database. By default, Home Assistant uses SQLite, and when you have a ton of sensor data flowing in, SQLite can start choking. My solution was to move everything to a VM on my home server. I also migrated Home Assistant's main database to MySQL, and for longer-term metrics and historical data, I set up an InfluxDB server. (I've documented the details of my [home server build in another blog post](/2024/05/04/home-server-journey/).) ## Scaling Up in a New House When I moved into a house, my Home Assistant installation grew significantly: more rooms, more lights, and more devices overall. Right now, I have over 100 devices connected to Home Assistant, including a large number of smart lights (all IKEA), plus an assortment of other smart devices. Practically every bulb in my home is now integrated into Home Assistant. ### Adaptive Lighting: Moving Beyond Flux One of the crucial features for me is **Adaptive Lighting**. Initially, I used Flux (an older solution for synchronizing lights with the time of day - see [my guide on Home Assistant, Flux and sensors](/2020/05/25/homeassistant-ikea-tradfri-flux-sensors/)), but I've since migrated to the new Adaptive Lighting integration available through HACS (Home Assistant Community Store). This newer system is much more sophisticated, offering better control over color temperature and brightness throughout the day. Managing this setup comes with two main challenges. First, neither Flux nor Adaptive Lighting can target light groups. Instead, you need to explicitly list every single light entity in your configuration. This becomes particularly tedious when you have dozens of lights that you want to manage together. It would have been much more convenient to just point the integration to a group and have it handle all the lights within that group automatically. The second challenge is that even though all my bulbs are from IKEA, they don't have all the same features. This means I need separate configurations for each category to get Adaptive Lighting working correctly. But the effort is worth it: circadian rhythms are important to me, and I really want that smooth, automatic shift in color temperature from warm yellows in the morning and evenings to cooler whites and blues during midday. ## Using Cursor to Speed Up Configuration One big leap for me this year has been leveraging [Cursor](https://www.cursor.com), an AI coding assistant, to handle the more tedious parts of Home Assistant's YAML configurations. I'll admit, I've never had the time to master every detail of Home Assistant's DSL or its configuration files. ### Writing a Custom Parser The first major task I tackled with Cursor was writing a custom script to parse all my lights, figure out exactly what kind of bulb each one is, and spit out debugging information. This is the foundation of building the correct adaptive lighting setup. Once the script categorizes the bulbs, I can then create or update the YAML configuration for each bulb type. Here's the script I use to analyze my Home Assistant lights. It connects to the Home Assistant API, categorizes all lights by their capabilities, and provides detailed debugging information about their current state and supported features: ```python import requests import json import os from datetime import datetime TOKEN = os.getenv("HA_TOKEN") # Set this to your long-lived access token (Bearer: ) # Function to get entity state def get_entity_state(entity_id): url = "http://localhost:8123/api/states/" + entity_id headers = { "Authorization": TOKEN, "content-type": "application/json", } response = requests.get(url, headers=headers) if response.status_code == 200: return response.json() else: print(f"Error getting state for {entity_id}: {response.status_code}") return None def get_all_lights(): """Get all light entities from Home Assistant.""" url = "http://localhost:8123/api/states" headers = { "Authorization": TOKEN, "content-type": "application/json", } response = requests.get(url, headers=headers) if response.status_code == 200: entities = response.json() lights = [] for entity in entities: entity_id = entity['entity_id'] if entity_id.startswith('light.'): lights.append(entity_id) return sorted(lights) else: print(f"Error getting entities: {response.status_code}") return [] def get_adaptive_switch_state(name): switch_id = f"switch.adaptive_lighting_{name.lower()}" state = get_entity_state(switch_id) if state: print(f"Adaptive switch {switch_id} full state: {json.dumps(state, indent=2)}") return state def check_light_capabilities(light_attrs, group_name, light_id, adaptive_config): """Check if light capabilities match adaptive lighting settings.""" warnings = [] # Check color temperature support if ('color_temp_kelvin' in adaptive_config and 'supported_color_modes' in light_attrs and 'color_temp' not in light_attrs['supported_color_modes']): warnings.append(f"WARNING: {light_id} in {group_name} group doesn't support color temperature, " f"but adaptive lighting is trying to set it. Supported modes: {light_attrs['supported_color_modes']}") # Check brightness support if ('brightness_pct' in adaptive_config and 'supported_color_modes' in light_attrs and 'brightness' not in light_attrs['supported_color_modes']): warnings.append(f"WARNING: {light_id} in {group_name} group doesn't support brightness, " f"but adaptive lighting is trying to set it. Supported modes: {light_attrs['supported_color_modes']}") return warnings def analyze_current_state(lights, group_name): print(f"\n=== {group_name} Current State ===") print(f"Analyzing at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") # Get adaptive lighting switch state switch_state = get_adaptive_switch_state(group_name) adaptive_config = {} if switch_state: print(f"Adaptive Lighting Switch: {switch_state['state']}") print(f"Last changed: {switch_state.get('last_changed', 'unknown')}") print(f"Attributes: {json.dumps(switch_state.get('attributes', {}), indent=2)}") adaptive_config = switch_state.get('attributes', {}) else: print("Adaptive Lighting Switch: Not found") now = datetime.now() current_time = now.strftime("%H:%M:%S") print(f"Current Time: {current_time}") # Track brightness statistics brightness_stats = { 'min': float('inf'), 'max': float('-inf'), 'total': 0, 'count': 0 } # Group lights by capabilities light_types = {} capability_warnings = [] for light in lights: state = get_entity_state(light) if not state: print(f"{light}: Not found or offline") continue attrs = state['attributes'] status = [] capabilities = [] # Check capabilities against adaptive lighting settings if adaptive_config: warnings = check_light_capabilities(attrs, group_name, light, adaptive_config) capability_warnings.extend(warnings) # Basic state if state['state'] == 'on': if 'brightness' in attrs: brightness_pct = round((attrs['brightness'] / 255) * 100) status.append(f"brightness: {brightness_pct}%") # Update brightness statistics brightness_stats['min'] = min(brightness_stats['min'], brightness_pct) brightness_stats['max'] = max(brightness_stats['max'], brightness_pct) brightness_stats['total'] += brightness_pct brightness_stats['count'] += 1 if 'color_temp_kelvin' in attrs: status.append(f"temp: {attrs['color_temp_kelvin']}K") elif 'color_temp' in attrs: status.append(f"mired: {attrs['color_temp']}") print(f"{light}: ON - {', '.join(status)}") else: print(f"{light}: OFF") # Detailed capabilities if 'supported_color_modes' in attrs: capabilities.append(f"modes:{attrs['supported_color_modes']}") if 'min_color_temp_kelvin' in attrs and 'max_color_temp_kelvin' in attrs: capabilities.append(f"temp:{attrs['min_color_temp_kelvin']}-{attrs['max_color_temp_kelvin']}") if 'supported_features' in attrs: capabilities.append(f"features:{attrs['supported_features']}") # Group by capabilities cap_key = ','.join(sorted(capabilities)) if cap_key not in light_types: light_types[cap_key] = [] light_types[cap_key].append(light) # Print capability warnings if capability_warnings: print("\n=== Capability Warnings ===") for warning in capability_warnings: print(warning) # Print brightness statistics if brightness_stats['count'] > 0: print(f"\n=== {group_name} Brightness Statistics ===") print(f"Minimum brightness: {brightness_stats['min']}%") print(f"Maximum brightness: {brightness_stats['max']}%") print(f"Average brightness: {brightness_stats['total'] / brightness_stats['count']:.1f}%") print(f"Number of lights on: {brightness_stats['count']}") # Print summary of light types print(f"\n=== {group_name} Light Types ===") for cap_key, lights in light_types.items(): print(f"\nCapabilities: {cap_key}") print("Lights:") for light in lights: print(f" - {light}") def group_lights_by_capability(lights): """Group lights by their capabilities.""" color_temp_lights = [] brightness_lights = [] other_lights = [] for light in lights: state = get_entity_state(light) if not state: continue attrs = state['attributes'] if 'supported_color_modes' in attrs: if 'color_temp' in attrs['supported_color_modes']: color_temp_lights.append(light) elif 'brightness' in attrs['supported_color_modes']: brightness_lights.append(light) else: other_lights.append(light) else: other_lights.append(light) return { 'Color Temperature': color_temp_lights, 'Brightness Only': brightness_lights, 'Other': other_lights } # Main execution if __name__ == "__main__": all_lights = get_all_lights() print("\n=== All Lights Analysis ===") print(f"Found {len(all_lights)} lights in total") # Group lights by capability grouped_lights = group_lights_by_capability(all_lights) # Analyze each capability group for group_name, lights in grouped_lights.items(): if lights: # Only analyze groups that have lights analyze_current_state(lights, group_name) ``` 1. **Run the custom parsing script** on my Home Assistant setup to produce a detailed list of bulbs and their capabilities. 2. **Feed the output** into Cursor (in "agent mode" or similar), along with my old configuration. 3. **Have Cursor generate** the updated YAML for the new Adaptive Lighting system. It's been a huge time-saver. Sure, I still do some manual debugging, but I also use Cursor to assist with the troubleshooting. For instance, if something breaks in Home Assistant, I feed the logs into Cursor and ask it to help me fix the error. It's surprisingly effective. ## IKEA Advice After extensive testing, I've optimized my adaptive lighting configurations for different IKEA bulb types. Here are my recommended settings that provide smooth transitions while maintaining good visibility throughout the day. ### Dimmable white spectrum For IKEA's _LED bulb GU10 345 lumen, smart/wireless dimmable white spectrum_ bulbs. ```yaml - name: adapt_brightness_standard_color_temp lights: - light.light_1 - light.light_2 min_brightness: 50 max_brightness: 100 min_color_temp: 2202 max_color_temp: 4000 sleep_brightness: 1 sleep_color_temp: 2202 transition: 45 interval: 90 initial_transition: 1 prefer_rgb_color: false ``` ### Dimmable color and white spectrum For the _LED bulb E27 806 lumen, wireless dimmable color and white spectrum/globe opal white_ bulbs. ```yaml - name: adapt_brightness_extended_color_temp lights: - light.light_3 - light.light_4 min_brightness: 70 max_brightness: 100 min_color_temp: 2000 max_color_temp: 6535 sleep_brightness: 1 sleep_color_temp: 2000 transition: 45 interval: 90 initial_transition: 1 prefer_rgb_color: false ``` ### Dimmable warm white For the basic _LED bulb GU10 345 lumen, smart/wireless dimmable warm white_ bulbs. ```yaml - name: adapt_brightness_brightness_only lights: - light.light_5 - light.light_6 min_brightness: 50 max_brightness: 100 sleep_brightness: 1 transition: 45 interval: 90 initial_transition: 1 ``` ## Next Steps: Smart TRVs Now that the lighting is running smoothly, my next big smart home project is upgrading all my radiators with Zigbee-based smart TRVs (thermostatic radiator valves). The goal is to have each room in my home maintain an optimal temperature by reading from the central Nest thermostat. In older British homes like mine, temperature control isn't very granular, so having each radiator adjust itself is a major comfort and efficiency boost. I've already purchased [these TRVs](https://s.click.aliexpress.com/e/_EzwaYAM) but haven't had time to configure them yet. My plan is: 1. **Pair the TRVs** to my Zigbee network. 2. **Pull temperature data** from my Nest thermostat (the main sensor). 3. **Set up automations** in Home Assistant so that each room's radiator valve opens or closes based on its own target temperature. I'm hoping this will help solve the typical British house problem: some rooms end up too warm, while others are never warm enough. With per-room heating control, it should be far more balanced and efficient. ## Conclusion (So Far) That's where my Home Assistant journey sits at the moment. I'm thrilled with how the adaptive lighting is working, especially now that I've harnessed an AI coding assistant to manage the complexity of my YAML files. The next challenge, smart radiator valves, will hopefully bring my home's temperature control on par with my lighting automation. --- # Using Google Forms for Waitlists and Launches URL: https://vpetersson.com/2025/01/14/using-google-forms-for-waitlists/ Date: 2025-01-14 Over the last decade, I've spun up quite a few landing pages to gauge interest for upcoming products and features. Back in the day, there were platforms like [Launchrock](https://www.launchrock.com/) that made it simple to collect email addresses and maintain a waitlist. While Launchrock (and later [Typeform](https://www.typeform.com/), etc.) provided decent user experiences at the time, the end goal was always the same: **collect user data into a spreadsheet (or CRM).** Nowadays, I still want the same functionality (i.e., capturing emails and other user info), but I want it to _look native_ on my site. Embedding a big Google Form or Typeform often looks clunky because it's just an iframe that's visually out of sync with the rest of the site. ## The Problem When I launched [Notipus](https://notipus.com), I faced this problem again. How could I have my own custom input fields on the site, yet still write the data directly into a Google Sheet (or form backend)? Of course, one could solve this by spinning up a simple backend service to handle form submissions, but I wanted to avoid the overhead of maintaining yet another service. The goal was to keep things simple and leverage existing infrastructure. I discovered a solution using **Google Apps Script** that effectively acts as a wrapper for a Google Form. This means I can collect submissions from a _native_ HTML form on my site and directly post it to a Google Form, which then writes to the associated Google Sheet. ## The Repo If you want to see this in the wild, take a look at the GitHub repo: [Viktopia/notipus.com](https://github.com/Viktopia/notipus.com). In particular, check out the form implementation ([form.html](https://github.com/Viktopia/notipus.com/blob/master/form.html)). ## The Script Below is the complete Google Apps Script I use. You'd attach this to the Google Form's script editor (explained in the next section). This script supports JSONP (i.e., `callback(...)`) so you can handle the response in a more flexible way client-side. ```js // Get your form ID from the URL of your Google Form const FORM_ID = FormApp.getActiveForm().getId(); function doGet(e) { // Get the callback name from the request const callback = e.parameter.callback || 'callback'; try { // Get the form data from parameters const firstName = e.parameter.firstName || ''; const lastName = e.parameter.lastName || ''; const company = e.parameter.company || ''; const email = e.parameter.email || ''; // Get the form const form = FormApp.openById(FORM_ID); // Create a new response const formResponse = form.createResponse(); // Get all form items const items = form.getItems(); // Map items to fields based on their titles items.forEach(item => { const title = item.getTitle().toLowerCase(); let response = null; if (title.includes('first name')) { response = item.asTextItem().createResponse(firstName); } else if (title.includes('last name')) { response = item.asTextItem().createResponse(lastName); } else if (title.includes('company')) { response = item.asTextItem().createResponse(company); } else if (title.includes('email')) { response = item.asTextItem().createResponse(email); } if (response) { formResponse.withItemResponse(response); } }); // Submit the form response formResponse.submit(); // Return success response wrapped in JSONP callback const jsonResponse = JSON.stringify({ success: true, message: 'Form submitted successfully', formId: FORM_ID }); return ContentService.createTextOutput(`${callback}(${jsonResponse})`) .setMimeType(ContentService.MimeType.JAVASCRIPT); } catch (error) { console.error('Error:', error); // Return error response wrapped in JSONP callback const jsonResponse = JSON.stringify({ success: false, error: error.toString(), formId: FORM_ID }); return ContentService.createTextOutput(`${callback}(${jsonResponse})`) .setMimeType(ContentService.MimeType.JAVASCRIPT); } } // Function to get form details function getFormDetails() { const form = FormApp.getActiveForm(); const items = form.getItems(); console.log('Form Details:'); console.log('Form ID:', form.getId()); console.log('Form Title:', form.getTitle()); console.log('\nForm Items:'); items.forEach(item => { console.log(`Title: ${item.getTitle()}, ID: ${item.getId()}, Type: ${item.getType()}`); }); } ``` ### Setting Up the Script in Your Google Form 1. **Create a Google Form** (or open an existing one). 2. **Open the Script Editor**: In the Form editor, click on the vertical dots in the top-right corner and select **Script editor**. 3. **Paste the Code**: Remove any existing code and paste in the script above. 4. **Save and Deploy**: - Click **Deploy > New deployment**. - Choose **Type**: "Web app". - Make sure you set: - **Execute the app as**: _Me_ (i.e., your Google account). - **Who has access**: _Anyone_. - Once deployed, you'll get a `webApp URL`. This is the endpoint you'll POST (or GET) to with your form fields. 5. **Grant Permissions**: The first time you deploy, you'll be asked to grant permissions. Since this script writes to the Form and the associated Sheet on your behalf, the permission request is expected. From here, your custom HTML form can submit data directly to this Google Apps Script endpoint. The script will then create a new submission in the actual Google Form (and thus populate your Google Sheet). No ugly iframes required! ## Wrapping Up With this approach, you keep a fully native look and feel on your landing page, while still leveraging the power of Google Forms and Sheets. There's still room for improvement (like adding Captcha), but it's a solid start. If you're looking to see how it's implemented in a real project, check out [Notipus on GitHub](https://github.com/Viktopia/notipus.com). --- # Nerding Out with Viktor is now available as audio-only, a.k.a Turning my video podcast into an audio podcast URL: https://vpetersson.com/2024/06/27/video-to-audio-podcast/ Date: 2024-06-27 In my previous article, [Launching a Video Podcast in 2024: My Journey and Lessons Learned](/2024/06/20/on-launching-a-video-podcast-in-2024/), I shared my experience of starting a podcast. Since then, I came across some intriguing [research](https://www.thepodcasthost.com/planning/should-i-make-a-video-podcast/) revealing that 43% of podcast listeners prefer audio-only formats. This got me thinking—by offering my podcast solely as a video feed, I might have been missing out on a significant audience. Given that I had already developed my own [podcast RSS generator](https://github.com/vpetersson/podcast-rss-generator/), this presented a perfect chance to nerd out a bit. Since my podcast RSS generation is already integrated into GitHub Actions, adding an audio-only feed was a straightforward process. Using the ever-reliable `ffmpeg`, I extracted the audio from the video files and repackaged it as an audio-only podcast RSS feed. Surprisingly, it really was that simple. After tweaking the URLs and adjusting the top-level title, I had a fully functional audio-only podcast feed ready for listeners. Best of all, there’s no extra work needed moving forward. Once the RSS feed was uploaded, the final step was to add it to [Spotify](https://open.spotify.com/show/6pj5BL8V1kLJJzc76MsVqj?si=48213c4556ea4c7b) and [Apple Podcasts](https://podcasts.apple.com/gb/podcast/nerding-out-with-viktor-audio-only/id1765124230) as a new feed—and just like that, it was good to go! ## GitHub Actions workflow Here's the complete GitHub actions workflow file I'm using for building and generating my podcasts for both video and audio. ```yaml --- name: Publish nightly on: push: branches: - master pull_request: branches: - master schedule: - cron: "00 01 * * *" env: XQ_VERSION: 1.2.3 YQ_VERSION: 4.44.3 R2_BUCKET: my-r2-bucket jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get -qq update sudo apt-get -qq install yamllint - name: Install xq run: | wget -q https://github.com/sibprogrammer/xq/releases/download/v${XQ_VERSION}/xq_${XQ_VERSION}_linux_amd64.tar.gz tar xfz xq_${XQ_VERSION}_linux_amd64.tar.gz - name: Ensure example file passes yamllint run: | yamllint -fgithub -d "{rules: {line-length: false}}" podcast_config.yaml - name: Run Podcast RSS Generator uses: vpetersson/podcast-rss-generator@master with: input_file: 'podcast_config.yaml' output_file: 'podcast_feed.xml' - name: Validate output with xq run: | cat podcast_feed.xml | ./xq - uses: actions/upload-artifact@v4 with: name: podcast_feed.xml path: podcast_feed.xml build-audio-feed: runs-on: ubuntu-latest needs: generate-audio steps: - uses: actions/checkout@v4 - name: Install yq run: | wget -q https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_linux_amd64 mv yq_linux_amd64 yq chmod +x yq - name: Install xq run: | wget -q https://github.com/sibprogrammer/xq/releases/download/v${XQ_VERSION}/xq_${XQ_VERSION}_linux_amd64.tar.gz tar xfz xq_${XQ_VERSION}_linux_amd64.tar.gz - name: Generate audio-only feed run: | cp podcast_config.yaml podcast_config_audio.yaml ./yq eval '.metadata.title = .metadata.title + " (audio only)"' -i podcast_config_audio.yaml sed -i -E 's|(https://podcast\.nerdingoutwithviktor\.com/S[0-9]{2}E[0-9]{2})\.mp4|\1.mp3|g' podcast_config_audio.yaml - name: Run Podcast RSS Generator uses: vpetersson/podcast-rss-generator@master with: input_file: 'podcast_config_audio.yaml' output_file: 'podcast_feed_audio.xml' - name: Validate output with xq run: | cat podcast_feed_audio.xml | ./xq - uses: actions/upload-artifact@v4 with: name: podcast_feed_audio.xml path: podcast_feed_audio.xml generate-audio: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install yq run: | wget -q https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_linux_amd64 mv yq_linux_amd64 yq chmod +x yq - name: Install dependencies run: | sudo apt-get -qq update sudo apt-get -qq install ffmpeg curl - name: Generate audio-only files run: | for url in $(yq eval '.episodes[].asset_url' "podcast_config.yaml"); do echo "Processing URL: $url" audio_url="${url%.mp4}.mp3" video_url="$url" if curl --output /dev/null --silent --head --fail "$audio_url"; then echo "$url already exists." else echo "$url does not exist. Converting $video_url to mp3..." ffmpeg -i "$video_url" $(basename $audio_url) echo "Conversion complete: $video_url" fi done - name: Install mc run: | wget -q https://dl.min.io/client/mc/release/linux-amd64/mc chmod +x mc - name: Set up mc env: R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }} R2_KEY_ID: ${{ secrets.R2_KEY_ID }} R2_KEY_SECRET: ${{ secrets.R2_KEY_SECRET }} run: ./mc alias set r2-nowv ${R2_ENDPOINT} ${R2_KEY_ID} ${R2_KEY_SECRET} - name: Copy converted MP3s run: | for file in *.mp3; do if [ -f "$file" ]; then ./mc cp "$file" r2-nowv/${R2_BUCKET}/ fi done deploy: runs-on: ubuntu-latest needs: - build - build-audio-feed if: github.ref == 'refs/heads/master' steps: - uses: actions/download-artifact@v4 - name: Install mc run: | wget -q https://dl.min.io/client/mc/release/linux-amd64/mc chmod +x mc - name: Set up mc env: R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }} R2_KEY_ID: ${{ secrets.R2_KEY_ID }} R2_KEY_SECRET: ${{ secrets.R2_KEY_SECRET }} run: ./mc alias set r2-nowv ${R2_ENDPOINT} ${R2_KEY_ID} ${R2_KEY_SECRET} - name: Copy podcast feed run: ./mc cp podcast_feed.xml/podcast_feed.xml r2-nowv/${R2_BUCKET}/ - name: Copy audio podcast run: ./mc cp podcast_feed_audio.xml/podcast_feed_audio.xml r2-nowv/${R2_BUCKET}/ ``` --- # Launching a Video Podcast in 2024: My Journey and Lessons Learned URL: https://vpetersson.com/2024/06/20/on-launching-a-video-podcast-in-2024/ Date: 2024-06-20 **Update:** Check out my blog post [All Roads Lead to DSLRs](/2025/06/18/all-roads-lead-to-dslrs/) _This blog post is inspired by the new wave of "Build in the Open" movement, where I want to share my experience and data._ In 2023, I embarked on a new adventure: launching a podcast. The idea was simple yet exciting — bring friends and experts to discuss topics they're passionate about, building my personal brand and promoting [Screenly](https://www.screenly.io) and [Viktopia Studio](https://studio.viktopia.io/) along the way. The result of this is my podcast [Nerding out with Viktor](/podcast/). ## Ground Rules I chose video because it felt more dynamic and less saturated than traditional audio podcasts. From the start, I set some clear rules for myself: - **Commitment:** Give it a full year before deciding to continue or stop. - **Consistency:** Release a new episode every two weeks without exception. These guidelines stemmed from research indicating that 90% or so of all podcasts don’t make it past a year and that platforms like YouTube reward regular content releases. ## The Power of YouTube I viewed YouTube as the main platform for my podcast. Being the second largest search engine, it offered great potential for discovery. Additionally, YouTube’s new Podcast feature, launched around the same time as my podcast, provided an extra boost for visibility. ## Six-Month Check-In: The Ups and Downs At the six-month mark, my podcast is a work in progress. I’ve hosted notable guests like [Eben Upton](/podcast/s01e12/) and [Mark Shuttleworth](/podcast/s01e13/), yet my subscriber and viewer numbers remain modest. I believe that with continued consistency, the podcast will gain momentum over the next six months, creating a flywheel effect. ### Current Statistics #### YouTube - Nearly 300 subscribers - Around 5k views and 217 watch hours in the last 28 days - Subscriber growth of about 50 per month #### Spotify - 504 plays in the last 30 days - 37 followers - Around 4,500 impressions in the last 30 days #### Apple Podcast - 23 followers - (Not enough data for additional metrics) ## Upgrading My Equipment I started with a Logitech Brio 4k Webcam but faced [limitations with 1080p](https://support.riverside.fm/hc/en-us/articles/9684909831325-Camera-Logitech-Brio-Logitech-4K-Pro) recording on Riverside. I switched to using my iPhone 14 Pro, which delivers excellent 4k quality. For audio, I began with an Audio-Technica ATR2100x-USB microphone, eventually upgrading to a Shure MV7 on a suspension arm. I prefer AirPod Pros for headphones to avoid the hassle of wires. ## Recording and Editing Process I use [Riverside.fm](https://riverside.fm/) for recording. Despite its Chrome-only limitation, it’s reliable and records in 4k, making it worth the investment. For editing, I rely on [ScreenFlow](https://www.telestream.net/screenflow/overview.htm). While Riverside offers basic editing tools, they’re limited and buggy for anything beyond short snippets. ScreenFlow, a tool I’ve used for screencasts over the years, provides the features I need without the complexity of professional editing software like Final Cut Pro X or Adobe Premiere Pro. Once I'm done editing, I use [MacWhisper](https://goodsnooze.gumroad.com/l/macwhisper) to generate a transcript from the episode. I then use this transcript in ChatGPT to generate summaries based on a set of prompts that I've tweaked. ## Navigating Distribution Challenges Distributing audio podcasts is easy, but video podcast distribution is more complicated. While there’s an RSS standard for video podcasts, it’s not widely supported. YouTube and Spotify require manual uploads, with Spotify only accepting h264 encoded videos, adding another layer of complexity. To meet Apple Podcasts’ requirements, I created my own [Podcast RSS Generator](https://github.com/vpetersson/podcast-rss-generator/), hosted on GitHub Action and Cloudflare’s R2, keeping my hosting costs below $10 in total. ## Strategies for Building Awareness Growing an audience has been the most challenging part. Here’s what has worked for me: - **Utilizing Riverside’s Excerpts:** Riverside creates short, subtitled excerpts, perfect for social media. I post these on LinkedIn, Twitter/X, and YouTube Shorts to drive traffic to the full episodes, scheduling them every other day. - **Optimizing Descriptions:** Detailed descriptions with relevant keywords are crucial, especially for YouTube, which functions as a search engine. Launching and growing a video podcast is a journey that requires dedication, consistency, and a fair amount of work to get going. Yet, I'm still optimistic that this has been a worthwhile investment. --- # Secure your Tailscale infra further with Mutual TLS (mTLS) URL: https://vpetersson.com/2024/05/29/tailscale-and-mutual-tls/ Date: 2024-05-29 A while back, I wrote about how I'm using Tailscale to [secure my local service](/2022/12/23/securing-services-with-tailscale/), thanks to Tailscale's built-in certificates. This greatly improved the security of my local environment. Combined with the rather sophisticated ACL policies, you can lock things down pretty well. That gets you pretty far, but I wanted to secure some services that didn't offer any real authentication (like [Gollum](https://github.com/gollum/gollum)), which I use for notes. This got me thinking about Mutual TLS (mTLS), which we use pretty heavily at [Screenly](https://www.screenly.io). Would it be possible to use the certificates from Tailscale for mTLS? As it turns out, yes! The first thing we need to do is to generate our client certificate. ## Generating on macOS If you installed Tailscale using the App Store on macOS, it does come with the CLI. You just need to do a bit more digging and work around some of the limitations of this and macOS. I whipped up a quick helper script that generates the certificate, along with a simple snippet that generates a PKCS#12 bundle, which is what Firefox, for instance, requires for client certificates. ```bash #!/bin/bash -ex TS="/Applications/Tailscale.app/Contents/MacOS/Tailscale" # Return the FQDN from Tailscale with some magic FQDN=$($TS status --json | jq -r '.Self.DNSName' | sed s/.$//g) CERT_PATH="$HOME/Library/Containers/io.tailscale.ipn.macos/Data" echo "Issuing certificates..." "$TS" cert "$FQDN" echo "Generating PKCS#12 bundle..." echo "You will need to enter a password here, which you will later use when importing the certificate" openssl pkcs12 -export \ -out "$CERT_PATH/$FQDN.p12" \ -inkey "$CERT_PATH/$FQDN.key" \ -in "$CERT_PATH/$FQDN.crt" \ -name "Tailscale cert" # Verify certificate echo "Let's verify the .p12 file for good measure." openssl pkcs12 -info \ -in "$CERT_PATH/$FQDN.p12" echo "Opening folder..." echo "Now you can import this certificate into Firefox." open "$CERT_PATH" ``` If you're on Linux, you should be able to just use the `tailscale cert` command to output your certificate to a directory of your choice and then run the `openssl pkcs12` command to generate the PKCS#12 bundle. Note that you will need to enter a password for the PKCS#12 certificate. If not, many clients (like Firefox) will fail to import the certificate. ## Importing into Browser Installing the PKCS#12 certificate is usually straightforward. On Firefox, you just do: 1. Go to Preferences > Privacy & Security > Certificates > View Certificates. 2. Click on Import and select the certificate.p12 file. 3. Enter the password you set during the creation of the PKCS#12 file. ## Setting up a Server for mTLS There are a lot of ways to set up a server to use mTLS, including Nginx, Envoy, or your favorite web server. The most lightweight way to set up mTLS in my experience is to use [ghostunnel](https://github.com/ghostunnel/ghostunnel/). We are going to assume you've set up your server as per [my previous guide](/2022/12/23/securing-services-with-tailscale/) and have your certs in `/etc/ssl/private` ready to go. With that ready, you can just fire up `ghostunnel` in Docker as follows: ```bash $ docker run -d \ --name ghostunnel \ -p 0.0.0.0:443:443 \ --network my-network \ -v /etc/ssl/private:/certs \ ghostunnel/ghostunnel server \ --listen 0.0.0.0:443 \ --target my-server:80 \ --cert /certs/$(hostname -f).crt \ --key /certs/$(hostname -f).key \ --unsafe-target \ --allow-cn my-device.ts-network.ts.net \ --allow-cn my-other-device.ts-network.ts.net ``` The above script spins up a `ghostunnel` instance that reverse proxies the traffic on the Docker network `my-network` to the container `my-server` on port `80`. We also automatically apply a ruleset with `--allow-cn` that only allows devices presenting a client certificate that matches either `my-device.ts-network.ts.net` or `my-other-device.ts-network.ts.net`. All other requests will be rejected. ## Testing with curl Now back to our client, let's make a test request using `curl`: ```bash $ curl \ --key "$HOME/Library/Containers/io.tailscale.ipn.macos/Data/$(hostname -f).key" \ --cert "$HOME/Library/Containers/io.tailscale.ipn.macos/Data/$(hostname -f).crt" \ https://my-server.ts-network.ts.net ``` Assuming this worked, you should now be able to move on to your browser and authenticate using your newly installed client certificate. Simply enter `https://my-server.ts-network.ts.net` and you should get a prompt like this: ![Using Tailscale certificate for Mutual TLS (mTLS) in Firefox](/assets/mtls-with-tailscale.webp) Congrats! You've now successfully authenticated using your Tailscale client certificate in your browser. Do however note that the client certificate will expire, so you will periodically need to repeat the process. Happy hacking! --- # Discover Email Providers Effortlessly with Email Service Checker URL: https://vpetersson.com/2024/05/16/email-service-checker/ Date: 2024-05-16 A decade ago, I created "Are They Using Google Apps" to address a simple yet crucial need: determining whether a remote party was using Google Apps (now Google Workspace) to decide if I should send them a Hangout invite for a meeting (or something else). Fast forward to today, and I am thrilled to introduce a revitalized and enhanced version of this concept – [Email Service Checker](https://emailservicechecker.com). Email Service Checker takes the original idea a step further by providing a comprehensive tool that allows you to determine the email service provider a domain or email address is using. This tool is perfect for sales teams and professionals who need to understand the email stack of their prospects for more targeted and effective outreach. ## Key Features of Email Service Checker - Identify Email Providers: Quickly find out if a domain is using popular email services like Google Workspace, Microsoft 365, Zoho Mail, and more. - Sales Prospecting: Use this tool to discover the email infrastructure of your potential clients, helping you tailor your sales pitch and communication strategy accordingly. - User-Friendly Interface: Simply enter a domain or email address, and Email Service Checker will provide you with the relevant email provider information in an easy-to-read format. - Open Source and Community-Driven: We believe in the power of community and open-source development. You can contribute to the project on GitHub and help us improve the service for everyone. ## How It Works - Enter Domain or Email: Visit [emailservicechecker.com](https://emailservicechecker.com) and enter the domain or email address you want to check. - Get Instant Results: Our tool will quickly analyze the domain’s MX records and provide you with the email provider information. - Use for Sales Prospecting: Leverage this information to understand your prospects better and tailor your outreach efforts for maximum impact. ## Get Started Today We are excited to bring this tool to the sales and marketing community. Whether you're trying to find out if a client is using Google Workspace or Microsoft 365, or you simply want to understand the email stack of your prospects, Email Service Checker is here to help. Visit [emailservicechecker.com](https://emailservicechecker.com) today and start discovering the email providers of your prospects effortlessly. Join the Community: Email Service Checker is open source and community-driven. We invite you to contribute to the project on GitHub and help us improve the service for everyone. Together, we can create a more efficient and effective tool for sales prospecting. Check out the GitHub repository [here](https://github.com/Viktopia/email-service-checker). --- # My Home Server Journey - From Raspberry Pi to Ryzen URL: https://vpetersson.com/2024/05/04/home-server-journey/ Date: 2024-05-04 ### Introduction Back when I was living in a snug studio flat in London, I began my home server adventure with a Raspberry Pi 3. This move to a settled lifestyle came after years of living as a digital nomad, during which I had fully minimized my personal belongings to adapt to a constantly mobile life. The shift to a more permanent base in London marked a new chapter where I could explore more stationary tech projects like setting up a home server. ### Early Stages Around that time, WD Labs - a now defunct R&D division of Western Digital focused on innovative projects for the Raspberry Pi - sent me a Pi Drive. They were a great bunch to collaborate with at [Screenly](https://www.screenly.io), always pushing the envelope on what could be achieved with Pi. ![WD Labs Pi Drive (opened)](/assets/wd-pi-drive-open.webp) The Pi Drive was designed for a single 2.5" drive. I decided to mod it to hold two 1TB drives, set up as ZFS mirrors for redundancy. Running [OpenMediaVault](https://www.openmediavault.org/) (OMV), this setup worked alright, albeit the Pi 3's USB 2.0 ports were a bottleneck, maxing out at 480mbps across both drives. Although slow, it was a solid entry into the world of network-attached storage using ZFS raid. ![WD Labs Pi Drive (closed)](/assets/wd-pi-drive-closed.webp) I still see discussions on Reddit and other forums where people consider using a Pi for NAS solutions. Based on my experiences, my advice would be to steer clear of using a Pi for this purpose—it's much more practical and efficient to invest in a cheap NUC instead. ### Upgrading and Expanding As my tinkering expanded (including my [home automation](/2020/05/25/homeassistant-ikea-tradfri-flux-sensors/)), the Pi's limitations became apparent. I needed a more robust system, so I upgraded to a NUC equipped with an i5 processor and 32GB of RAM running [Proxmox](https://proxmox.com/en/). I migrated the ZFS pool from the Pi, expanded it by adding another drive while maintaining the original drives. In order to keep things tidy (and get good air flow), my friend [Ivan](https://twitter.com/ipedrazas) 3D printed a great stand that could hold up to four 2.5" drives. ![3D printed 2.5" drive stand](/assets/3d-printed-drive-stand.webp) Over the years, I've had to replace at least four dead/failing 2.5" drives, but ZFS made these changes seamless without any data loss. Despite adding an SSD as cache, the setup underperformed due to USB bandwidth limitations, and the NUC eventually succumbed to the constant heavy load. ### NUC to Protectli When my NUC eventually gave in, I just happened to have a Protectli [FW6E-0](https://eu.protectli.com/product/fw6e/) on hand from another project. It came equipped with an Intel i7 processor and I had added 32GB of RAM, making it a suitable candidate to take over my home server duties. I moved my ZFS pool to this new machine and continued running Proxmox. This setup proved robust, handling my requirements well for about two years, but eventually, I felt the need for a more integrated solution to consolidate several devices into one solid, future-proof system. ![Protectli FW6e-0](/assets/protectli-fw6e-0.webp) ### Evaluating and Finalizing the New Setup In my search for a consolidated solution, I evaluated setting up a beefy compute node alongside something like a Synology NAS. However, I decided against this arrangement due to my previous (bad) experiences with running VMs on Synology NASes over iSCSI. Since my VMs can comfortably fit across a few SSDs, I decided to look for an all-in-one solution, where I'd use the spinning drives for backups. Perhaps equally importantly, a decent spec'd Synology NAS would cost me almost as much as the final setup I ended up with (but then I'd still have to add the compute node). After extensive research, I chose the Jonsbo N2 case for its compact yet accommodating form factor that could hold up to 5x 3.5" SATA drives. As a nice bonus, the 3.5" drives are also easily swappable from the front, removing the need to open the case when a drive eventually dies. The next challenge was selecting the right motherboard. All the options I considered required some form of compromise. I was particularly looking for a motherboard with at least two PCI slots to accommodate an additional NIC for Link Aggregation Group (LAGG) with the onboard NIC and an M2 expansion card. Ultimately, I had to make a tough choice and forego LAGG due to slot limitations, settling on the ASROCK B550M-ITX/ac, which could handle my AMD Ryzen 7 5700G after a crucial BIOS update (more on that below). ![Jonsbo N2](/assets/jonsbo-n2.webp) ### And of Course Something Went Wrong While this should have been flagged in my research, it turns out the B550M-ITX/ac supports the AMD Ryzen 7 5700G, but not without a BIOS update. The motherboard supports "instant flash," a straightforward method, but lacks support for more sophisticated flashing methods like "BIOS Flashback" or "Q-Flash." These methods would have allowed me to update the BIOS without needing a supported CPU installed. After scouring countless Reddit threads and tech forums for a solution, I realized that my only option was to procure a cheaper Ryzen 3 processor solely to perform the BIOS update. This workaround, although not ideal, was necessary to get my server running with the intended CPU. I also evaluated the Ryzen 9, but since the motherboard only had one PCIe port, I was unable to fit a dedicated GPU. This left me to choose from AMD CPUs with built-in GPUs, where the 5700G is currently one of the fastest options available. The decision aligns with a future upgrade path, where I plan to swap out the CPU and motherboard for newer ITX models as they become available, ensuring that the server can continue to meet evolving performance demands. ![Jonsbo N2 (top)](/assets/jonsbo-n2-top.webp) ### Security Considerations and Future Plans In my pursuit of a secure and efficient server, I really wanted TPM-backed Full Disk Encryption (FDE) for all drives along with Secure Boot (which is now supported natively in Proxmox). The complexities of implementation and reliability issues led me to give up on FDE for the root drive for now. The state of TPM-backed FDE in Linux is depressingly primitive compared to macOS and Windows, where such security measures "just work" seamlessly. After spending a few hours both with Debian and Proxmox (which is based on Debian), I deemed it too unreliable for a headless server. LUKS based FDE with a passphrase works great, and I've used that for years on non-headless devices. However, once you try to use the TPM to unlock the FDE, things get complicated (and very hackish). I briefly entertained going down the [NixOS](https://nixos.org/) route instead of Proxmox, after having my friend Jon sold me on the benefits of Nix and NixOS over beers (and on [my podcast](https://www.youtube.com/watch?v=9l-U2NwbKOc)) for some time. His post "[Secure Boot & TPM-backed Full Disk Encryption on NixOS](https://jnsgr.uk/2024/04/nixos-secure-boot-tpm-fde/)" definitely makes for a compelling argument, but I ended up sticking to Proxmox for now due to time limitations. I'm also still using my external USB drive setup as I haven't gotten around to ordering the 3.5" drives yet. My plan is to configure ZFS across these new drives with TPM-backed Full Disk Encryption for added security (and to save time on shredding drives when they start failing). I will add an update later with details about this. ### Final Bill of Materials (BOM) for the New Server Setup: - **Case**: [Jonsbo N2](https://www.aliexpress.com/item/1005004980577095.html) - Compact form factor ideal for multiple internal drives. - **Motherboard**: [ASROCK B550M-ITX/ac](https://www.aliexpress.com/item/1005006480641176.html) - Supports AMD Ryzen CPUs, compact ITX form factor, essential for limited space setups. - **CPU**: AMD Ryzen 7 5700G - Powerful processor with 16 cores - suitable for heavy loads, with integrated graphics. - **RAM**: 2x 32GB DDR4 - **Power Supply**: CWT 650W Modular 80 Plus Gold - **Boot/Root drive**: 500GB M2 NVMe (plugged into the motherboard) - **Additional Storage**: - 3x M2 NVME drives for VMs in a [Linkreal PCIe x16 to 4-Port M.2 NVMe SSD Adapter](https://www.aliexpress.com/item/1005003877778925.html). - Due to a limitation in the BIOS/motherboard, it appears as I'm only able to use 3 out of the 4 M2 drive slots. - 4x 4TB 3.5" SATA 7200 RPM drives (connected straight to the motherboard). - This will either be a Stripe + Mirror ZFS pool (or possibly RAIDZ1 with a hot spare). - **KVM**: PiKVM V4 Mini This compact setup is designed to be robust, scalable, and capable of handling substantial data loads and various server tasks efficiently. The choice of components ensures that the server is future-proofed and can accommodate additional expansions or upgrades as needed. As things stand, the only thing that bothers me (other than the lack of TPM based FDE) is the noise level. Compared to its predecessor, it is a lot louder. This can probably be rectified with a quieter CPU fan, but that's a problem for the future. ### Update I've now added 4x 4TB 3.5" SATA (7200 RPM) drives to the system. So far everything seems to work great. This is what I used to create my tank: ```bash $ zpool create -o ashift=12 \ mirror \ /dev/disk/by-id/ \ /dev/disk/by-id/ \ mirror \ /dev/disk/by-id/ \ /dev/disk/by-id/ ``` If you want to learn more about ZFS and its capabilities, I highly recommend checking out [my podcast episode with Allan Jude](/podcast/s01e25/), where we dive deep into ZFS architecture, features, and best practices. I also ended up setting up an encrypted dataset that I use for some VMs, as well as for backups. ```bash $ zfs create \ -o encryption=on \ -o keylocation=prompt \ -o keyformat=passphrase \ /encrypted ``` When you reboot the box, you now need to run: ```bash $ zfs load-key -r /encrypted ``` The performance so far looks very reasonable with a max at ~250MB/s (for writes), which is sufficient for backups and some less read/write intensive VMs. To migrate over the data from my old ZFS tank, I just used the built-in tool in Proxmox. It was just a matter of stopping the VM/CT, move it and start it back up. So far, the only drawback is that I need to manually enter the passphrase to mount the encrypted ZFS volume, which prevents some VMs from booting up automatically on say a power failure. ### Update 2 One of the drawbacks with using a consumer grade motherboard/server is that they generally speaking do not come with an IPMI. This can be a bit of a PITA when you want to tweak BIOS settings (or fix a boot problem). To solve for this, I decided to get myself a [PiKVM](https://pikvm.org/) V4 Mini and attach to the server. So far I'm pretty impressed with it. The UI works well and you are even able to connect it to Tailscale. If you're looking to solve for this problem, I can definitely recommend one of these. ### Update 3 Remember how I mentioned the noise level being a problem? Well, the fan that came with the Jonsbo case has been driving me crazy for ages. Not only is it incredibly loud, but it also doesn't do any fan control—it just runs at full blast all the time. However, when I came across [Brian Moses' DIY NAS: 2026 Edition](https://blog.briancmoses.com/2025/11/diy-nas-2026-edition.html), I realized I could swap my fan for a [Noctua NF-A12x25 PWM](https://www.amazon.com/dp/B09C6DQDNT?tag=diynas2026-20&language=en_US). The only caveat was that in my Jonsbo case, the Noctua fan was too thick to fit inside the case, so I had to mount it on the outside where it pokes out a bit. However, when I connected this fan directly to the motherboard, the noise level went down to almost nothing. It's been a game-changer for my home server setup, and I can now barely hear it running. --- # Introducing sbomify - The Future of SBOM Management URL: https://vpetersson.com/2024/04/05/sbomify/ Date: 2024-04-05 I'm excited to share with you something that has been in the works for some time - [sbomify](https://sbomify.com), my latest venture aimed at transforming how Software Bill of Materials (SBOMs) are managed, shared, and worked on. As we navigate through the complexities of modern software development, the need for maintaining stringent security and compliance has never been more apparent. It's this challenge that spurred me to create sbomify, a platform that simplifies SBOM management in a way that's not just efficient, but also fosters greater transparency and collaboration among everyone involved. # Automate, Consolidate, Collaborate With sbomify, integrating SBOM management into your CI/CD pipeline becomes a breeze, enabling automatic uploads of the latest SBOMs. This feature ensures that you always have the most up-to-date information at your fingertips, providing a real-time glimpse into your software components. Moreover, sbomify excels in bringing together multiple SBOMs into one comprehensive document, offering a unified view of software components from diverse sources like Docker and application stacks, which is crucial for thorough analysis and decision-making. A more detailed analysis about the SBOM lifecycle can be found [here](https://sbomify.com/features/generate-collaborate-analyze/) # Built for Us What truly sets sbomify apart is its innate capacity for collaboration. It's a platform designed not just with tools and processes in mind, but with people at its core. Whether you're a supplier, developer, part of a security team, or a customer, sbomify is our space to connect, discuss, highlight issues, and confirm compliance, all within a singular, user-friendly environment. # Embark With Me As I launch sbomify, I warmly invite you to come aboard and explore how we're paving the way for the future of SBOM management. It's a solution for all of us, promising to elevate our approach to software security management. Let's embrace this journey to a more secure, compliant, and efficient future together with sbomify, where managing security is as simple as managing updates. Stay tuned for further updates, and feel free to visit [sbomify](https://sbomify.com) for a deeper dive into how we're changing the SBOM management landscape. Welcome to our next step in software security and compliance. Welcome to sbomify. --- # On UniFi Captive Portals URL: https://vpetersson.com/2024/03/27/unifi-captive-portal/ Date: 2024-03-27 ## What are captive portals? Lately, I've been doing some digging into captive portals - you know, these things that pop up when you try to connect to the WiFi at a coffee shop or hotel. In essence, they are very simple: - You load a web page that usually requires you to tick a box or enter your email. - If the system is satisfied with the input above, you are sent to a page that returns HTTP status 200 (and usually a `success` message in the body), which tells the system that you're online. Now, I'm a massive UniFi fan. Over the years, I've migrated most of my networks over to UniFi equipment with [Dream Machine Pros](https://store.ui.com/us/en/collections/unifi-dream-machine/products/udm-pro) as the firewall. It's a great turn-key solution that largely just works. Prior to this, I usually relied on either a [pfSense](https://www.pfsense.org/) (and later [OPNsense](https://opnsense.org/)), but frankly, the UDM is just a great device that integrates seamlessly with all other UniFi hardware. That means provisioning a whole network is done in a few clicks. ## Captive portals and UniFi Bringing this back to the topic of captive portals, UniFi's console does allow you to provision captive portals out-of-the-box (called 'Hotspot Portal'). However, their support is rather limited. Usually, the reason any commercial WiFi deployment would want to use a captive portal for their WiFi is to capture email addresses (for marketing), or to set tracking pixels (for remarketing). UniFi's default captive portal does not allow you to do either of this, making it somewhat moot to enable. ![UniFi's built-in hotspot/captive portal](/assets/unifi-built-in-captive-portal.webp) What they do support, however, is something they call 'External Portal Server,' which allows you to integrate a third-party captive portal with the UniFi hardware. This is all neat until you start digging into the details. Or should I say, the lack of details. You see, UniFi provides absolutely _zero_ documentation on this. There are, however, third-party services/libraries that have reverse-engineered how this flow works, but it's less than ideal. ## Using External Portal Servers What you notice when you start digging into these third-party tools is that they all ask you for admin access to your UniFi Console (which also needs to be publicly accessible). That should be a pretty big red flag right there, as this essentially gives them full control over your network. In theory, they could own your entire network and do all sorts of mischief, like redirecting `gmail.com` to a phishing site. There is, however, a good reason why they ask for this. As it turns out, this is required for an External Portal Server to work; they need the ability to approve guests by issuing an API call to the console. When a user tries to access the WiFi, a GET request is sent to the external server that looks like this: `http://externalportal.example.com?ap=access_point_mac&user_mac=user_mac_address&ssid=network_ssid&url=original_url_requested` Notice all those GET parameters: - ap=access_point_mac - user_mac=user_mac_address - ssid=network_ssid - url=original_url_requested With that information, the external portal then needs to issue back a POST request to the console that looks something like this: ``` POST /api/s//cmd/stamgr Authorization: Bearer Content-Type: application/json { "cmd": "authorize-guest", "mac": "", "minutes": } ``` Notice that we need to pass the MAC address back, along with how long the session should be open. Now, there are a few problems with this: - We need to be able to talk directly to the console (i.e., this is **not** unifi.ui.com), but rather the console directly. - We need to acquire a Bearer token (which is done by issuing a call to the `/api/auth/login` with a set of admin-level credentials). As you can see, this is why these tools need both direct access to your UniFi console and a set of credentials. Some of them also use this to provision the External Portal Server configuration, which is somewhat neat. ## Is it possible to use External Portal sensibly? The short answer is that the only sensible way to do this is if you run your External Portal on the local network. It's still far from ideal, but that way, you can at least avoid having to expose your console to the public internet. It wouldn't be very difficult to implement the above flow, but I don't think anyone who cares even the slightest about security should a) expose their console to the public internet and b) hand over admin credentials to a third party. What I'm really hoping for is that UniFi will allow API access to their cloud-based console (unifi.ui.com), and provide proper API documentation. Currently, the implementation is prone to breakage, especially since the API isn't even versioned. Additionally, the lack of ACL/permissions for user accounts is a significant issue. Ideally, one should be able to issue an API token that can only approve clients. Solving these issues would make it viable to offer this as a cloud-based service. For now, I might develop a simple Python application to test the waters, as you can access the console's API over the LAN. Or I might just give the NodeJS implementation below a go. My initial idea of creating a SaaS product around this (I even bought the domain [CaptivePortalConnect.com](https://captiveportalconnect.com/) for the purpose) is likely not worth pursuing due to the fragile (and insecure) approach required. ## Update (2024-07-01) I _thought_ I had made a [breakthrough](https://github.com/woodjme/unifi-hotspot/issues/68#issuecomment-2143952594) in the API access confinement by leveraging the new (and of course undocumented) 'hotspotoperator' permission (rather than having to use a full-blown admin account), it turned out that this [didn't work](https://github.com/woodjme/unifi-hotspot/issues/68#event-13343000818) in the end (kudos to [@woodjme](https://github.com/woodjme)).. ## References - [unifi-hotspot](https://github.com/woodjme/unifi-hotspot) - NodeJS based captive portal that can feed to Google Sheets. Looks very promising. - [UniFi API Client](https://github.com/Art-of-WiFi/UniFi-API-client) - A PHP based UniFi client that is actively maintained by [Art of WiFi](https://artofwifi.net), who offer captive portals as a service (based on the library). - [aiounifi](https://github.com/Kane610/aiounifi) - Python based UniFi library used by Home Assistant. - [unificontrol](https://github.com/nickovs/unificontrol) - Another Python based UniFi library. --- # Boostrapping Chronicles - Chapter 2 URL: https://vpetersson.com/2024/03/24/bootstrapping-chronicles-chapter2/ Date: 2024-03-24 In 2011, when Apple launched the Mac App Store, it opened doors for developers. Around the same time, our project, YippieMove, had plateaued because Google released its own email migration tools, taking away a lot of our customers. Seeing the Mac App Store as a big opportunity, we decided to jump in and create something unique for its launch. That's how we came up with Blotter. It was a simple yet smart idea: a calendar that sits right on your desktop wallpaper, always visible but never in the way. It combined functionality with sleek design, but pulled all data from the native macOS applications (Calendar and Reminders), to avoid having to re-invent the wheel. Blotter was ready for the Mac App Store's first day and it was a hit. It ranked among the top 10 productivity apps in the US, only beaten by Apple's own applications briefly. We also made the top 10 store wide in numerous location worldwide. Over the course of a few years, we kept improving Blotter, fixing early shortcuts in the implementation (with a lot of help from Ilya Kulakov). Blotter on the Top 10 list in Productivity, only beaten by Apple's own applications. ![](/assets/blotter-top-10.webp) One big issue we faced was the Mac App Store's rule against charging for app updates. This meant we couldn't earn from new versions of Blotter unless we released it as a completely new app. This was a massive challenge for us, as it really limited out options. Mind you, Blotter was a few years by now, and there was willingness to pay for an updated version. Eventually, we moved Blotter into maintenance mode, focusing only on essential updates due to the above limitation. It took until macOS Sonoma in 2023 for Apple to introduce a feature like Blotter as part of the system, which felt like a nod to what we had built. From Blotter, I learned a lot about the power and limitations of selling through an App Store. It's a great way to get your app out there, but it also puts a cap on how much you can grow. Looking back at the success of Blotter, it really came down to two things: - Only do one (or few) thing(s), but do it well (the UNIX philosophy). - Making the UI beautiful. Looking back at it, I’m not sure we could have done much more with Blotter. The biggest lesson was the lack of recurring revenue. Some macOS apps have tried to solve this by charging a subscription model, but I’m personally not a fan of this Apps I used to love (like [Git Tower](https://www.git-tower.com/mac)) adopted this and I stopped using it. While I happily pay for plenty of SaaS products, I just mentally struggle with a recurring charge for a desktop applications. In chapter 3, which will be the final chapter, I will unpack our experience with bootstrapping [Screenly](https://www.screenly.io). --- # Boostrapping Chronicles - Chapter 1 URL: https://vpetersson.com/2024/03/22/bootstrapping-chronicles-chapter1/ Date: 2024-03-22 ## Preface: Why Bootstrap? I’ve written a few short articles about my experience bootstrapping businesses. I’ve both bootstrapped and raised money (both sides of the pond). These are my learnings (with context) and hopefully, they are helpful for others. The first question one might ask is when should you bootstrap. In my experience, it makes sense for low-to-mid complexity products (or if you have deep pockets). There are no doubt products and services that you cannot bootstrap, such as deep tech with a slow time to market, training AI models that will run you millions of dollars in compute resources, or where you aggressively need to pay for market share to corner a market. If you’re building a relatively straightforward product, it makes a lot of sense to bootstrap. In fact, it probably doesn’t make sense to raise money if you (and/or your team) are able to write some or most of the code yourself to generate the first revenue. Put another way, why would you want to give up control of your company for a small angel round? The second you accept the term sheet, you’ve picked your path. It will be challenging to change it, as you need to convince others. If you bootstrap, you are in control of your destiny. That, and the fact that you don’t need a $100m exit to change your life. If you sell for $10m bootstrapped, you will often pocket more than you would at $150m if you take VC money (as you likely be at Series C at least, and are heavily diluted). You will, on the other hand, make a lot of LPs and fund managers rich if you sign that term sheet and end up selling. I’ve seen entrepreneurs close $100+ million exits and walk away with just a few hundred thousand, and bootstrapped businesses sell for just north of $10m where the founder kept it all. The worst of both worlds is to raise money at shitty valuations, get a small amount of money, and yet live like you're bootstrapping (yes, I’m looking at European angels/VCs). If you're raising money, and they tell you that your salary should be rent + 10% or whatever, do yourself a favor and walk away. It’s one thing to be broke while bootstrapping your own business (I’ve been there), but there’s absolutely no reason to do that while making someone else rich. ## Bootstrapping Lessons, Chapter 1: YippieMove While still in college in the early 2000s, Alex and I created our first business called YippieMove. It was an email migration tool that targeted students. The idea came when graduating, and I realized my account would be shut down and my email would be wiped (this is probably no longer a problem). The idea was to offer cheap email migration for students ($10). As an experienced entrepreneur, it’s easy to point out countless flaws in our plan, but it did give us a taste of fundraising in the valley. (My biggest regret from this was probably not applying for YC, as we would have been in the same cohort as Airbnb.) Fast forward a few years, and we ended up pivoting to self-served email migrations for SMB and colleges (we had Harvard and a few other big names as customers). In the end, it was a business that flatlined early. Our only saving grace was that we rode on a wave of Google Apps (now Google Workspace) adoption, and Google didn’t have any tooling for this. What I learned from this endeavor was a few things: 1. Never build a business selling to students. Their willingness to pay is minimal at best and they don’t value their time. 2. Channel sales made our business viable (shoutout to Crisantos among others). 3. Marketplaces are a powerful sales channel for small niche services. In today’s world, where G2/Capterra is fully gamified, I’m not sure where that leads. Back then, Google's own listings drove a large part of our revenue. 4. Do Your Homework on TAM and GTM: The advantage of raising funds is it forces you to thoroughly understand your Total Addressable Market (TAM) and Go-To-Market (GTM) strategy. This exercise is invaluable. Often, developing the product is the straightforward part, while devising an effective GTM strategy is more complex. For us, the TAM for non-enterprise email migration proved too limited. 5. Establish a Recurring Revenue Stream: While our channel sales did result in repeat business, they didn’t scale as much as we needed. In hindsight, pivoting to a backup service—which some of our clients used us for (hello, Naval!)—might have been a more sustainable model for generating recurring revenue. I also got to know Kevin Henrikson, who (fortunately) passed on investing in YippieMove, but turned out to become a friend, mentor and board member for Screenly. I’ll continue my lessons in Chapter 2 with my next experience with building Blotter. --- # Launching 'Nerding Out with Viktor' - A New Tech Podcast (and the Innovative Podcast RSS Generator) URL: https://vpetersson.com/2024/01/02/new-podcast/ Date: 2024-01-02 Hello folks, Viktor here! I'm thrilled to bring two exciting updates to you all today. First, let's talk about my new project, ["Nerding Out with Viktor,"](/nerding-out-with-viktor/) a video podcast where I dive into riveting tech discussions with experts from various fields. The first episode features a deep dive into Cloud Native security with Andrew Martin from ControlPlane. We cover a range of topics, including ethical hacking and cybersecurity strategies. It's a real treat for anyone keen on tech and security. You can catch the podcast on platforms like [Apple Podcasts](https://podcasts.apple.com/us/podcast/nerding-out-with-viktor), [Spotify](https://open.spotify.com/show/nerdingoutwithviktor), and [YouTube](https://www.youtube.com/@nerdingoutwithviktor). Make sure to subscribe and join in on the conversation! Also, I'm excited to introduce the [Podcast RSS Generator](https://github.com/vpetersson/podcast-rss-generator/), a tool I developed for podcasters (or rather, for myself). It's designed to generate RSS feeds for audio/video podcasts, especially useful for those using S3, Google Cloud Storage, or Cloudflare R2 for hosting. This tool stands out in its field, being possibly the first of its kind and is even featured in the GitHub Actions Marketplace. It's adaptable to various storage solutions, making it a versatile choice for podcast creators. Check it out in the [GitHub Actions Marketplace](https://github.com/marketplace/actions/podcast-rss-generator). Stay tuned for more exciting episodes and updates on these projects! Your support and feedback are always appreciated. Let's keep nerding out together! --- # Enough is Enough - Killing the Annoying macOS Word Definition Pop-Up Once and For All URL: https://vpetersson.com/2023/12/06/lookup-insanity/ Date: 2023-12-06 Ever been deep in your workflow on your Mac, meticulously selecting text in Apple Mail or iTerm, only to be ambushed by some random word definition pop-up? Yeah, me too. And frankly, it's been driving me up the wall. It’s like playing Whac-A-Mole with dictionary entries you never asked for – the same annoyance as right-clicking a word and accidentally hitting "Look Up". I scoured forums and guides for ages, trying to find a way to kill this feature. It felt like a quest without a map, because, let's face it, who knows where to look for such obscure settings? Today, I hit my breaking point. I dove back into the digital trenches and finally struck gold in a [blog post](https://www.techjunkie.com/how-to-turn-off-multitouch-trackpad-look-up-in-os-x/). Turns out, this infuriating feature has been lurking in macOS for several iterations. Why, Apple, why? But here's the silver lining – the way to reclaim your peace: 1. Storm into **System Preferences** like a boss. 2. Slam that **Trackpad** option. 3. Hunt down **"Look up & data detection"**. 4. Ruthlessly switch it from **"Tap with three fingers"** to **"Off"**. Boom! No more pop-up sneak attacks. Sanity, welcome back to my digital realm. --- # Securing and exposing local services with Tailscale and Nginx URL: https://vpetersson.com/2022/12/23/securing-services-with-tailscale/ Date: 2022-12-23 Securing and encrypting communication on local network devices is a hard problem. Plenty of people tried, including myself in our now sunsetted company [WoTT](https://wott.io/). The root of the problem is a combination of DNS and routing to local IPs, which means you can't use automated certificate issuers, like Let's Encrypt. The solution, it seems, comes from an unexpected source: the VPN/Wireguard service provider Tailscale. I've been a fan of [ZeroTier](https://www.zerotier.com/) for some time and use it both personally and professionally to access nodes behind firewalls. Recently, I kept hearing from more and more people how much they love [Tailscale](https://tailscale.com). After hearing [@jnsgruk](https://hachyderm.io/@jnsgruk)’s glowing reviews of Tailscale at Ubuntu Developer Summit, I decided to give it a try. It's clearly a much more refined product than ZeroTier. The user interface and user experience are smoother, and it feels more production-ready. After enrolling a few nodes, I was convinced it was time to start migrating. There isn't much to write about how to add a machine to Tailscale, as that part is straightforward. What I will focus on in this article is how to use two really neat features in Tailscale to solve the problem in the opening paragraph: - [MagicDNS](https://tailscale.com/kb/1081/magicdns/) - [SSL Certificates](https://tailscale.com/kb/1153/enabling-https/) (powered by Let's Encrypt) Together, this is a game changer. For some time, I've been planning to secure my local web services (Home Assistant, Proxmox etc) with self-signed SSL certificates issued by a local CA (using [cfssl](https://github.com/cloudflare/cfssl)). However, this opens a Pandora's Box of security issues. As you now need to trust a local self-signed CA on all your machines, this could, in theory, be exploited for some really nasty MiTM attacks if someone were to get their hands on the root CA keys. As it turns out, Tailscale solves this for me, but instead of using self-signed certificates, I get proper certificates issued from Let's Encrypt. The way it works is rather elegant. When you enable MagicDNS, you get a domain assigned (e.g. `foobar.ts.net`). All your devices will then get a hostname there (e.g. `my-server.foobar.ts.net`). Since this is a valid FQDN, Tailscale can use it to issue proper certificates from Let's Encrypt with the command `tailscale cert my-server.foobar.ts.net`. Just like with regular Let's Encrypt certificates, these are semi short-lived and thus need to be renewed periodically. As such, we need to automate this renewal on all our hosts with a simple systemd service: ```yaml # /etc/systemd/system/tailscale-cert.service [Unit] Description=Tailscale SSL Service Renewal After=network.target syslog.target [Service] Type=oneshot User=root Group=root WorkingDirectory=/etc/ssl/private/ EnvironmentFile=/etc/default/tailscale-cert ExecStart=/usr/bin/tailscale cert ${HOSTNAME}.${DOMAIN} [Install] WantedBy=multi-user.target ``` ```yaml # /etc/systemd/system/tailscale-cert.timer [Unit] Description=Renew Tailscale SSL Certificates [Timer] OnCalendar=weekly Unit=tailscale-cert.service Persistent=true [Install] WantedBy=timers.target ``` ```yaml # /etc/default/tailscale-cert # Configuration for Tailscale SSL Renewal HOSTNAME=my-server DOMAIN=foobar.ts.net ``` With these two files created, you can manually start the service to ensure it works: ```bash $ systemctl daemon-reload $ systemctl start tailscale-cert.service $ systemctl enable tailscale-cert.timer ``` If everything went well, you should now have your certificates in `/etc/ssl/private`. We can then set up a basic Nginx configuration to expose an internal service running on say `localhost:8080` by editing `/etc/nginx/sites-enabled/default` (or similar): ```nginx server { listen 80 default_server; listen [::]:80 default_server; return 301 https://$host$request_uri; } server { listen 443 ssl default_server; listen [::]:443 ssl default_server; server_name my-server.foobar.ts.net; ssl_certificate /etc/ssl/private/my-server.foobar.ts.net.crt; ssl_certificate_key /etc/ssl/private/my-server.foobar.ts.net.key; # Consider including the hardening config that Let's Encrypt # recommends for enhanced security. location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://localhost:8080; } } ``` You should now be able to navigate to your server using a **proper** SSL certificate at the address https://my-server.foobar.ts.net. Pretty neat. There's even an [official Nginx auth](https://tailscale.com/blog/tailscale-auth-nginx/) that can be used. I haven't experimented with this yet, but it could presumably be used to further secure your Nginx reverse proxies. Below are some notes that I encountered while setting this up on various systems. ## Home Assistant By default, Home Assistant will complain if you try to access it over a proxy. To overcome this, you need to add this in `configuration.yaml`. ```yaml http: use_x_forwarded_for: true trusted_proxies: - 127.0.0.1 ``` You may also need to tweak your Nginx config for working with WebSocket. ### Home Assistant and MySQL with TLS If you're intending to connect to a MariaDB/MySQL instance using TLS, [the official documentation](https://www.home-assistant.io/integrations/recorder/#mariadb-omit-pymysql-using-tls-encryption) is incorrect. As [this forum post](https://community.home-assistant.io/t/mysql-through-an-ssl-connection/453607) correctly points out, instead of `;ssl=true`, you need to append `&ssl=true`. However, note that Home Assistant _does not_ actually verify the SSL certificate, thus making it vulnerable to a MiTM attack. ### Home Assistant and InfluxDB with TLS To use InfluxDB with TLS, you need to make the following changes to your Home Assistant YAML file: ```yaml influxdb: api_version: 2 ssl: true host: influxdb.foobar.ts.net port: 8086 ``` ## Proxmox Setting up Tailscale on Proxmox was just like any other system. The somewhat tricky part was to consume the certificate. To accomplish this, I added the following line after `ExecStart` in `/etc/systemd/system/tailscale-cert.service`, which instructs Proxmox to use the certificates issued from Tailscale: ```yaml ExecStartPost=pvenode cert set /etc/ssl/private/${HOSTNAME}.foobar.ts.net.crt /etc/ssl/private/${HOSTNAME}.foobar.ts.net.key --force 1 --restart 1 ``` Somewhat unrelated to the certificates, but in order to install Tailscale on an LXC container, you need to run it in privileged mode as per [this document](https://tailscale.com/kb/1130/lxc-unprivileged/) and add to the config: ``` lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file ``` Note that you can't move a container to privileged mode from unprivileged, as this will break file permissions and other things in the container. The way to accomplish this is to take a backup of the container, and then **restore** the container as privileged. ## MariaDB/MySQL To use the certificate with MariaDB/MySQL, we need to modify our setup slightly to accommodate permissions. ```yaml # /etc/systemd/system/tailscale-cert.service [Unit] Description=Tailscale SSL Service Renewal After=network.target After=syslog.target [Service] Type=oneshot User=root Group=root Environment="HOSTNAME=mariadb" ExecStart=/usr/local/sbin/tailscale-mysql.sh [Install] WantedBy=multi-user.target ``` ```bash #!/bin/bash set -euo pipefail IFS=$'\n\t' mkdir -p /etc/mysql/ssl tailscale cert \ --cert-file /etc/mysql/ssl/cert.crt \ --key-file /etc/mysql/ssl/cert.key \ ${HOSTNAME}.foobar.ts.net chown mysql:mysql -R /etc/mysql/ssl chmod 0660 /etc/mysql/ssl/cert* ``` With this done, we just need to tell MariaDB/MySQL to use these certificates: ```yaml # /etc/mysql/mariadb.conf.d/50-server.cnf [...] # We need to point to a CA path instead of the CA file since we # are using a proper certificate. ssl-capath = /etc/ssl/certs/ ssl-cert = /etc/mysql/ssl/cert.crt ssl-key = /etc/mysql/ssl/cert.key require-secure-transport = on # We don't want to allow lower ciphers than 1.2 as per NIST etc tls_version=TLSv1.2 [...] ``` Finally, restart the service with `systemctl restart mysql`. You might also want to recreate your MySQL user and add the constraint `REQUIRE SSL;` to ensure the remote users only can connect using TLS (even though that should in theory also be done using `require-secure-transport`). ## InfluxDB 2 Much like MariaDB/MySQL, setting up InfluxDB requires a bit extra work with permission. We start with a unit that fires a Bash script ```yaml #/etc/systemd/system/tailscale-cert.service [Unit] Description=Tailscale SSL Service Renewal After=network.target After=syslog.target [Service] Type=oneshot User=root Group=root Environment="HOSTNAME=influxdb" ExecStart=/usr/local/sbin/tailscale-influxdb.sh [Install] WantedBy=multi-user.target ``` We then create the bash script: ```bash #!/bin/bash set -euo pipefail IFS=$'\n\t' mkdir -p /etc/influxdb/ssl tailscale cert \ --cert-file /etc/influxdb/ssl/cert.crt \ --key-file /etc/influxdb/ssl/cert.key \ ${HOSTNAME}.foobar.ts.net chown influxdb:influxdb -R /etc/influxdb/ssl chmod 0660 /etc/influxdb/ssl/cert* ``` Upon starting this script, we should now get our certificates in place. The only thing we need to do now is to edit the configuration file to have it consume our certificate: ```toml # /etc/influxdb/config.toml bolt-path = "/var/lib/influxdb/influxd.bolt" engine-path = "/var/lib/influxdb/engine" tls-cert = "/etc/influxdb/ssl/cert.crt" tls-key = "/etc/influxdb/ssl/cert.key" ``` Restarting the service will automatically make InfluxDB serve content over HTTPS. ## OPNsense I'm yet to add support for this. It should certainly be possible, but BSD isn't an officially supported platform. Moreover, because OPNSense uses its own CA for things like VPN configuration, it might be a bit more challenging. ## CUPS Configuring [CUPS](https://www.cups.org/) to use Tailscale was rather straightforward. After installing the systemd unit and timer, you should have your certificate available. Next, open up `/etc/cups/cups-files.conf` and add `CreateSelfSignedCerts no` to prevent CUPS from issuing its self-signed certificates. Next, delete all existing self-signed certificates by running `sudo find -type f /etc/cups/ssl -delete`. We now need to set up a symlink to our existing Tailscale certificates to a place where CUPS is looking for them (i.e. `/etc/cups/ssl`). This is done by running: ```bash sudo ln -s /etc/ssl/private/my-box.foobar.ts.net.{crt,key} /etc/cups/ssl/ ``` Finally, we need to make some tweaks to `/etc/cups/cupsd.conf`: - Modify the `ServerAlias` stanza to match your Tailscale hostname, such as `ServerAlias my-box.foobar.ts.net.ts.net` - Set the `Listen` stanza to only listen on the Tailscale interface, by doing `Listen my-box.foobar.ts.net.ts.net` - We also probably want to set `Browsing On` so that we can easier find the printers Lastly, we need to edit all the relevant `` blocks and add `allow all` to the configuration. Initially, I was planning to use the `@IF(tailscale0)` macro ([cupsd.conf](https://www.cups.org/doc/man-cupsd.conf.html)), but I wasn't able to get this working and didn't dive much further into it. With all those changes live, you can just restart CUPS (`sudo systemctl restart cups`), and you should be good to go. To add the printer on macOS, just add it as an IP printer with the hostname being the Tailscale hostname and then the `/printers/` as the Queue. --- # Wordpress, 'The response is not a valid JSON response' and Cloudflare URL: https://vpetersson.com/2022/09/29/response-is-not-a-valid-json/ Date: 2022-09-29 In the last 24 hours, I've spent an embarrassing amount of time trying to debug a simple WordPress installation. When saving any changes, I received `The response is not a valid JSON response`. In addition, I also received weird symptoms I received was: - Getting 404s on requests to `/wp-json` - Presumably the root cause for `The response is not a valid JSON response` - Strange redirect loops with the header `x-redirect-by: WordPress` - Clearly a redirect originating from with in WordPress rather than Apache or Cloudflare After spending a long time researching this, I discovered about a hundred useless answers to why people received this. In retrospect, one of the common replies was that it was related to SSL. However, in my case SSL worked just fine, so that didn't make a lot of sense. Except that it was right on the money, in a convoluted way. See, the server was running a regular LAMP installation with Let's Encrypt providing the SSL certificate. That part worked. I knew the certificate was valid etc. I then use Cloudflare for DNS/CDN/DDoS protection. Routing to the site worked just fine. **However**, by default, Cloudflare is set its SSL/TLS [encryption mode](https://developers.cloudflare.com/ssl/get-started) to 'Flexible'. This means that Cloudflare will gladly reverse proxy an HTTP end-point. In my case, what happened was that Cloudflare would serve the content as HTTPS to the client, but use **HTTP** in the reverse proxy. As far as WordPress is concerned, the content is then served as HTTP, thus causing some internals to break. (On a technical note, what I would imagine happening is that WordPress simply ignore common flags, such as `X-FORWARDED-PROTO` that could have helped here.) The solution was to change the 'encryption mode' from 'Flexible' to 'Full (strict)', which means that Cloudflare will HTTPS to communicate to the back-end (while also validate the certificate). This is exactly what I wanted. Hopefully this will save someone else a bunch of head scratching. --- # How to grant SSH access to a 'regular' user on OPNsense URL: https://vpetersson.com/2021/11/17/opnsense-and-remote-ssh/ Date: 2021-11-17 I was working on trying to grant a 'regular' user SSH access in [OPNsense](https://opnsense.org/) last night. After banging my head against the wall for some time (partially because the [official documentation](https://docs.opnsense.org/manual/how-tos/user-local.html) is outdated), I was able to figure it out. (Do however note that this is _different_ than how you grant a user SSH access in [pfSense](https://www.pfsense.org/), where the steps do align with the outdated documentation.) Here's how you do it: - Go to System -> Access -> Groups - Create a new group called 'remote_access' - Go to System -> Access -> Users - Create a new user with a valid shell (i.e. not `nologin`), and make sure to add a valid SSH key and to add the user to the group 'remote_access' - Go to System -> Administration and navigate to the 'Secure Shell' Section. - Under 'Login Group', select 'wheel,remote_access' This of course assumes that you have SSH already enabled and remotely accessible. However, assuming this is true, you should now be able to login using the newly created user. --- # Solving 'dpkg-divert error unable to change ownership of target file' on Raspberry Pi URL: https://vpetersson.com/2021/05/07/raspberry-pi-fix/ Date: 2021-05-07 I've run into the following error myself a number of time in recent time, and just wanted to document the solution for this in case other people run into it too: Here's the problem: - You are trying to upgrade your Raspberry Pi to the latest version - When you upgrade the kernel, it chokes with an error like this: ``` > $ sudo apt upgrade Reading package lists... Done Building dependency tree Reading state information... Done Calculating upgrade... Done 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 5 not fully installed or removed. After this operation, 0 B of additional disk space will be used. Do you want to continue? [Y/n] Setting up raspberrypi-kernel (1.20210430-1) ... Removing 'diversion of /boot/kernel.img to /usr/share/rpikernelhack/kernel.img by rpikernelhack' dpkg-divert: error: unable to change ownership of target file '/boot/kernel.img.dpkg-divert.tmp': Operation not permitted dpkg: error processing package raspberrypi-kernel (--configure): installed raspberrypi-kernel package post-installation script subprocess returned error exit status 2 Setting up raspberrypi-bootloader (1.20210430-1) ... Removing 'diversion of /boot/start.elf to /usr/share/rpikernelhack/start.elf by rpikernelhack' dpkg-divert: error: unable to change ownership of target file '/boot/start.elf.dpkg-divert.tmp': Operation not permitted dpkg: error processing package raspberrypi-bootloader (--configure): installed raspberrypi-bootloader package post-installation script subprocess returned error exit status 2 dpkg: dependency problems prevent configuration of libraspberrypi0: libraspberrypi0 depends on raspberrypi-bootloader (= 1.20210430-1); however: Package raspberrypi-bootloader is not configured yet. dpkg: error processing package libraspberrypi0 (--configure): dependency problems - leaving unconfigured dpkg: dependency problems prevent configuration of libraspberrypi-bin: libraspberrypi-bin depends on libraspberrypi0 (= 1.20210430-1); however: Package libraspberrypi0 is not configured yet. dpkg: error processing package libraspberrypi-bin (--configure): dependency problems - leaving unconfigured dpkg: dependency problems prevent configuration of libraspberrypi-dev: libraspberrypi-dev depends on libraspberrypi0 (= 1.20210430-1); however: Package libraspberrypi0 is not configured yet. dpkg: error processing package libraspberrypi-dev (--configure): dependency problems - leaving unconfigured Errors were encountered while processing: raspberrypi-kernel raspberrypi-bootloader libraspberrypi0 libraspberrypi-bin libraspberrypi-dev E: Sub-process /usr/bin/dpkg returned an error code (1) ``` After wasting far too much time trying to debug this, I found that there was an easy solution: simply remount `/boot`. ``` > $ sudo umount /boot > $ sudo mount /boot ``` Now, I don't know what the exact root cause is, but it's likely something to do with the fact that `/boot` is mounted as vfat and for whatever reason there is some kind of lock or similar that happens after you've run the device for some time. In any case, hopefully this saves others the agony of trying to resolve this. --- # Solving NUC USB boot issue with PXE boot URL: https://vpetersson.com/2020/11/27/pxe-booting-nuc/ Date: 2020-11-27 I recently wanted to reinstall my trusty old Linux workstation with Debian 10. I imagined this would be a straight-forward thing to do. Just download the ISO and flash it out to a USB stick and be off to the races. Well. It wasn't. As it turns out, some [NUCs do not like modern USB sticks](https://community.intel.com/t5/Intel-NUCs/NUC-not-booting-from-USB/td-p/502254). After trying two or three different USB sticks I had laying around, none of them were picked up by the system (neither as booting devices or for flashing the BIOS). Since I bought a few "good" USB sticks last year, I gave away or threw away the "crap" ones...the ones that the NUC would have accepted. Since really needed to re-install the machine such that I could use it for some heavy Docker builds, I needed to come up with a workaround. This is when I realized that I could in theory install Debian using PXE boot. I was a bit hesitant at first, but then I ran across [netboot.xyz](https://netboot.xyz/). In short netboot.xyz is a boot image that allows you to select among a large number of distributions and tools. ![](/assets/netboot.webp) As it turns out, configuring PXE booting on my [pfSense](https://www.pfsense.org/) ended up [being a breeze](https://figura.im/posts/2020/01/pfsense-as-a-netboot.xyz-server/). Within a matter of minutes, I was able to boot my NUC into the Debian Buster installer. As a bonus, I'm also able PXE boot VMs instead of having to download ISOs. --- # Databat is back! URL: https://vpetersson.com/2020/06/04/databat-is-back/ Date: 2020-06-04 ![](/assets/bat.svg) [A few years ago](/2018/03/19/sonar-databat-people-counter/), I created a people tracker called [Sonar](https://github.com/databat-io/sonar) and open sourced it. The goal of the project was to monitor foot traffic in retail environments. After deploying it and more or less forgetting about it for almost two years, I was reminded about it last month and decided to resurrect it. The good news is that the device that I deployed in a retail environment as a test was still ticking along just fine. A lot of things have changed since I last touched the project, which allowed me to remove a number of the hackish workarounds and make it more robust. As I was able to make the project more reliable, I also started to work on offering this as service. I've created [databat.io](https://databat.io), and populated it with a [waitlist](https://databat.io/waitlist/) for anyone interested in this service. The idea is to keep the project hybrid, where the code that goes on the Raspberry Pi open source, but it can be configured to report data into a central interface, which will be useful for users with multiple devices (similar to the model we use at [Screenly](https://screenly.io)). **Recent updates:** - Refactored runtime to use Docker (and Docker-compose), which is compatible with [Balena](https://balena.io) - Added support for running on Raspbian/Raspberry Pi OS - Added support for Raspberry Pi 4 Model B - Improved Bluetooth logic to make it more robust - Added PostgreSQL support (if you want to use an external database) --- # Achieving success with Home Assistant, Flux and sensors URL: https://vpetersson.com/2020/05/25/homeassistant-ikea-tradfri-flux-sensors/ Date: 2020-05-25 My philosophy for home automation from the start has been that **the best UI is no UI**, meaning that I just want things to work automagically. I don’t want to fiddle buttons or software on a day-to-day basis. Sensors and automations should do most of the work. In addition to automation, I wanted [f.lux](https://justgetflux.com/)/Flux/Night Shift for my lights, meaning that the lights should follow my [circadian rhythm](https://nigms.nih.gov/education/fact-sheets/Pages/Circadian-Rhythms.aspx) and change color over the course of the day to mimic the sun. As often happens with tech projects, this turned out to be a lot more complicated. ![](/assets/home-assisant-overview.webp) _Screenshot of one of the views in Home Assistant_ I must now admit that I’ve spent/wasted (depending on how you look at it) countless hours getting things to a state where I’m happy. Hopefully this article will save you some time if you’re looking to accomplish the same. At the end of this article, you should be able to: - Not screw up your [Home Assistant](https://www.home-assistant.io/) setup like I did - Configure Home Assistant to work with your IKEA Tradfri lights - Setup Flux with IKEA Tradfri lights - Monitor air quality in all your rooms and be able to take actions on this (e.g. turn on a fan when the CO2/humidity/temperature level gets above/below a threshold) - Integrate Home Assistant with Unifi to determine presence based on WiFi connection (e.g. when my phone disconnects from the WiFi I’m considered away) ## Picking your home automation software If you’re just diving into home automation, you will likely be somewhat overwhelmed with the amount of options out there. In addition to all hardware vendor's solutions (e.g. IKEA, Philips Hue etc), Apple also threw themselves into the game with their Home.app. My advice is that you should try to keep things simple, and stay with any of these if you can, as it will save you countless of hours. Unfortunately, neither the IKEA app, nor Apple’s Home.app provided me what I wanted, so I had to keep looking. What I landed on was Home Assistant (HA), which has arguably become the most popular (open source) home automation platform. I must admit that I have a love-hate relationship with HA. While it is a very powerful platform that integrates with almost anything under the sun, it suffers from the same problem as many open source projects do: - (Sometimes) outdated documentation - Poor quality control I’m not blaming the developers for this, as it is a complex product that is fast moving. It is likely very hard to test all integrations (as it connects to hardware after all). Without going into a rant about my issues, it’s suffice to say that my home setup has been broken countless times by updates (either because of bugs or non-backward compatible changes), and that I’m very reluctant to update the system these days. ## Setting up Home Assistant Judging by the forums, the most popular way to run HA is using a Raspberry Pi. This is probably fine if you have a very simple setup (in which case, IKEA/Hue/Apple might be easier). However, if you’re aspiring to setup something similar to what I will be describing here, I strongly discourage using a Raspberry Pi. While the Pi 4 with a sufficient amount of RAM might provide you with sufficient amount of resources, the storage will likely be both too slow and fragile (i.e. the SD card is likely to wear out quickly). In addition, the way I’ve setup HA requires that you have access to the host directly to launch additional Docker containers, which is something the Raspberry Pi version of HA doesn’t allow for. In theory, you might run multiple Raspberry Pis instead, but I decided to simply throw it all into a single VM on an x86 server instead. In summary, I strongly recommend that you setup HA in a VM rather than a Raspberry Pi. When we’re done, we will be running tools like InfluxDB and MySQL/MariaDB side-by-side with HA, which is likely to bring a Raspberry Pi to its knees (in particular from an I/O perspective). Launching HA on a VM with Docker installed is very straight forward. Here’s the launch script that I’m using: ``` #!/bin/bash VERSION=stable echo "Upgrading from: $(docker inspect home-assistant | jq '.[0].Image')" docker pull homeassistant/home-assistant:${VERSION} docker kill home-assistant docker rm home-assistant docker run \ --init -d \ --restart always \ --name="home-assistant" \ -v /usr/local/homeassistant:/config \ -v /etc/localtime:/etc/localtime:ro \ --net=host \ homeassistant/home-assistant:${VERSION} # These steps may no longer be needed docker exec home-assistant pip3 install --no-cache-dir -q --upgrade pip docker exec home-assistant pip3 install --no-cache-dir -q -U pymysql echo "Now running: $(docker inspect home-assistant | jq '.[0].Image')" docker system prune -f ``` You can find more information on how to run HA with Docker [here](https://www.home-assistant.io/docs/installation/docker/). ## Configure Home Assistant to store events in MySQL/MariaDB One of the issues I ran into relatively early was the limitation of the built-in database. By default, HA is using an SQLite database. This is perfectly fine if you have a simple setup. However, as we start pushing more and more events to HA, such as sensor data and network device data (from Unifi in my case), what you will notice is that at some point is that the HA UI will grind to a halt and become extremely slow. For me, the Logbook and History tabs would not even load. After taking a closer look, I discovered that my SQLite database was several hundred megabytes, which explained the timeout. Fortunately, HA does support MySQL/MariaDB out-of-the box. All you need to do is to setup a MySQL/MariaDB database and reconfigure the [recorder](https://www.home-assistant.io/integrations/recorder/) to instead use the database. This had a tremendous effect on performance. My recommendation is that you start with MySQL/MariaDB right away, as you will have to jump through a number of hoops to migrate your SQLite data into the new database later. It’s possible (I did it), but just not a straight forward process. ## Configure Home Assistant to send metrics to InfluxDB While we’re on the topic of databases, I’ve also configured my HA to send metric data to [InfluxDB](https://www.home-assistant.io/integrations/influxdb/). Using this configuration, I’m able to visualize HA data (such as temperature) in Grafana (which can retrieve data from InfluxDB). ![](/assets/grafana-home-assistant.webp) _Example of Home Assistant sensor data being rendered in Grafana (via InfluxDB)_ ## Notes on IKEA lights While there are countless "smart bulbs" out there, I settled on the IKEA Tradfri, largely because they were cost effective (a fraction of the cost of Philips Hue). When you setup something like Flux, it’s important to know which lights you have. For instance, the setup for Philips Hue uses a different `mode`. What’s important to point out is that IKEA lights come in two different versions (as far as I know): - One that only provides shades of warm white (e.g. [this one](https://www.ikea.com/gb/en/p/tradfri-led-bulb-gu10-400-lumen-wireless-dimmable-warm-white-60420041/)) - One that provides shades of white _and_ yellow (e.g. [this one](https://www.ikea.com/gb/en/p/tradfri-led-bulb-gu10-400-lumen-wireless-dimmable-white-spectrum-90408603/)) It’s important that you know which one(s) you are using, as you cannot mix them in the same Flux group. I unfortunately bought both types, but in retrospect, I should have stuck to a single type. It is also worth noting that neither of these are RGB (unlike say the Philips Hue), which is why they are a lot cheaper. Personally, I don’t care for RGB lights, so this was fine by me. It’s also worth saying that you will need the IKEA Gateway for this to work. As a side-note, I must stress that under no circumstances do you want to re-scan the QR code of your gateway after the setup, even if the app will prompt for it. By doing so, you reset the IDs of the lights, and you will need to do the mapping again. ## Setting up Flux with IKEA lights I’m not going to waste cycles in this article on how to setup the IKEA lights in HA, as that’s a very straight forward process. Instead, let’s focus on getting the circadian rhythm configuration to work. As noted in the previous section, before you begin, you need to know what lights you have. If you’re having a combination of both, do make sure you know which light is which. It will break the Flux if you include the wrong type. [Flux](https://www.home-assistant.io/integrations/flux/) is built directly into HA. All we need to do is to configure and enable it. While that sounds easy, I have banged my head against the wall for countless hours trying to get it to work, and hopefully this will save you the effort (because I used the wrong `mode` and tried to group all my lights together). Since I have both types of IKEA lights, I need to have two distinct Flux configurations. For the yellow+white spectrum lights, the configuration looks like this: ``` switch: - platform: flux lights: - light.tradfri_bulb_10 [...] name: Flux Mired start_time: '8:00' stop_time: '22:00' mode: mired ``` For the yellow-only lights, the configuration looks like this: ``` - platform: flux lights: - light.tradfri_bulb [...] name: Flux XY start_time: '8:00' stop_time: '22:00' brightness: 200 mode: xy ``` (Note that both of these goes into the same `switch` block above) ## Monitoring air quality As someone that [works from home](/remote-work/), I’m in control of my work environment. I want to ensure that it is optimized both for ergonomics and for health at large. One thing that I’ve been curious about for some time has been the impact of air quality. Many moons ago, I purchased a [Foobot](https://foobot.io/) to get some insight into this. While it did the job, and it does indeed integrate with HA, it was a bit cost prohibitive to put one of these in every room (not to mention that it is rather large). To solve this problem, I decided to [build my own](/2019/11/16/home-assistant-and-esphome/) that is both smaller and cheaper. (Since publishing the blog post, I’ve received a number of requests from people who wanted the same. As many users just wanted this off-the-shelf and not solder together the components, I’ve teamed up with the fine folks at Pi Supply to manufacture the sensor. You can pre-order from [here](https://uk.pi-supply.com/products/iot-home-air-quality-sensor-for-home-assistant).) With these sensors live, I’m able to do things like: - Visualize the historical temperature/humidity/VOC/CO2 values - Automate the fan in my office, such that it turns on/off automatically when either the temperature or CO2 level reaches a certain threshold ![](/assets/home-assistant-history.webp) Again, make sure you’ve configured the MySQL/MariaDB recorder before you start adding sensors, as your HA will quickly become sluggish as the data builds up (in particular if you’re trying to get a histogram or similar). ## Setting up Unifi with Home Assistant One neat thing about HA is that it integrates with the Unifi Controller, such that you can configure actions ([Presence Detection](https://www.home-assistant.io/getting-started/presence-detection/)) based on who’s connected to the WiFi, which in turn maps to HA's own "Persons" feature. There are other ways to accomplish this, including Bluetooth and HA's mobile app, but given that I’m already using Unifi hardware, this was the obvious choice for me. Setting this up was straight forward, and the details can be found [here](https://www.home-assistant.io/integrations/unifi/). I must however note that I have had this integration break multiple times due to bugs. The reason why I wanted to setup Presence Detection was such that I can trigger automation when arriving or leaving the home. For instance, when I leave, the following things happen: - All lights turn off - The music is paused on my Sonos - I turn off all outlets with smart plugs Similarly, when I arrive home, the following thing happens: - Flux gets re-enabled (disabled when away) - All lights turn on - The outlets are turn on again ## Sonos integration Finally, I got speakers in basically every room, as I love being able to have some low background music on all day. HA integrates nicely with Sonos, and there’s not much to speak about it as far as the setup goes. If your Sonos is on the same VLAN as your HA node, it will be detected automatically when you enable the [Sonos integration](https://www.home-assistant.io/integrations/sonos/). With the integration enabled, you will display what’s being played in the UI, as well as automate actions (such as pausing music when you leave your house). ## Conclusion HA is a powerful platform. While it will likely take you some time to set it up to your likings, I have run into few limitations. There is an [active community](https://community.home-assistant.io/) that is quick to answer questions if you run into issues. After about 1.5 years with HA, I am now at a point where I almost have no complaints. It took me some time to get here, but these days I only interact with HA a few times a week. The rest is handled by automation, which is exactly what I was looking for. --- # Restoring access to PosgreSQL after Helm upgrade URL: https://vpetersson.com/2020/05/22/restoring-helm-postgress-access/ Date: 2020-05-22 If you've used PostgreSQL in Kubernetes with Helm, chance are you've [locked yourself out](https://github.com/helm/charts/tree/master/stable/sentry#postgressql) after performing an upgrade. The reason for this is that if you do not specify a password explicitly using `postgresqlPassword`, Helm will rotate this password for you when you run `helm upgrade`. Not ideal. This has happened to me a few times over the years. To restore access, you need to to jump in to your PostgreSQL container (`kubectl exec -ti your-postgres-container -n your-namespace bash`) and temporarily alter the authentication. This of course is not ideal, so we want to move as swiftly as possible. From within the container, run the following commands: ``` $ sed -i 's/md5/trust/' /opt/bitnami/postgresql/conf/pg_hba.conf $ pkill -HUP postgres ``` This will allow us to access the PostgreSQL server **without** authentication and reload the config. Next, login to the PostgreSQL server and set a password: ``` $ psql -h 127.0.0.1 -U postgres psql (11.5) Type "help" for help. postgres=# ALTER USER postgres WITH PASSWORD 'my-password'; ALTER ROLE postgres=# \q ``` Finally, kill the PostgreSQL server, which should automatically terminate your connection: ``` $ pkill postgres command terminated with exit code 137 ``` When it comes back, the changes to `pg_hba.conf` should have been reverted, and you should now be able to access the server using the password you set above. --- # VMWare Fusion error - cannot open /dev/vmmon URL: https://vpetersson.com/2019/12/08/cannot-open-dev-vmmon/ Date: 2019-12-08 This weekend I needed to use Fusion for the first time since I upgraded to macOS Mojave. Having run Fusion 8 for many years and being happy with it, I was somewhat annoyed with needing to upgrade to Fusion 11. At least that appeared to be the consensus on the interwebs. In retrospect, I'm not 100% sure. That said, I've received great value from Fusion, and I don't mind paying money for good software. Unfortunately, after upgrading I received a similar error as before with Fusion 8: "Cannot open /dev/vmmon: No such file or directory". After spending a fair bit of time researching this error, the verdict on the VMWare forum appears to say that you cannot have VMWare Fusion **and** VirtualBox install simultaneously and that you need to uninstall VirtualBox. Since I occasionally use VirtualBox (through Vagrant), that felt like a sub-bar solution. Fortunately, I found [this blog post](https://www.kastelo.net/blog/2017-05/coexisting-virtualbox-vmware-fusion-macos/) that outlines how to simply unload/load the VirtualBox kernel modules on demand instead. However, after doing this, my error still remained. As it turns out, I have missed some kind of System Preferences dialogue where I allowed Fusion to load its kernel modules. Details can be found [here](http://www.programmersought.com/article/9068165406/). Hence, if your error still remains, make sure that you double check that. Doing this would have saved me time. --- # Interview with John Agosta from Canonical/Ubuntu on working remotely URL: https://vpetersson.com/remote-work/2019/11/26/interview-with-john-agosta/ Date: 2019-11-26 In this interview on [remote work](/remote-work/), I'm speaking to John Agosta from Canonical (the company behind Ubuntu). As a long-time Canonical team member, John and my paths have crossed multiple times over the years, starting when I was at a cloud company, and he was at the server division at Canonical, to more recently when he moved to the Ubuntu Core (formerly known as Snappy) side, as I was building out [Screenly](https://www.screenly.io) where John now is the Program Manager. While Canonical isn't a fully remote company (they have multiple offices around the globe), a large part of their workforce is working either partially or fully remotely. As such, I wanted to understand more about their internal structure for this. Because I've had a lot of communication with John over the years, and he's one of the fully remote team members, he's an ideal candidate to better understand the inner workings of Canonical's remote culture. ### Can you tell me a bit more about yourself? I’m an old school software developer. By that I mean, I started software development back in the days of mainframe computing; Fortran, C, assembler, and when 256K was a lot of memory, and 10MB was a large disk. I originally thought I would be an architect designing the next generation residential homes. Little did I envision that the architecture I ended up loving more would utilize computer software building blocks. Over my three decade career I have stayed in the software development field as a developer, product manager, and deliver manager. ### How long have you been working remotely? For 8.5 years. ### Where are you based out of? Steamboat Springs, Colorado a small Colorado ski town. ### In your opinion, what are some characteristics of a well functioning remote team? Characteristics of a well functioning co-located team and a remote team are very similar. Where they diverge is probably more in the levels of clarity communication. Communication is core to every aspect of the team characteristic. - Business clear direction and goals - Organization leaders need to understand that they need to provide clear set of directions. This allows for team members at all levels to make important decisions at their levels and not wait for answers. - Strong open communication - Every member of the team needs to be both a good listener and comfortable contributing to discussions. - Self motivated - There is no one looking over your shoulder to see that you are at your desk working. Remote workers need the ability to focus on the required business tasks - Ability to shutdown - If you sleep and work in the same place it can be difficult to know when to quit. Where brick and mortar managers worry their staff are not working if they cannot see them working. Remote managers need to be concerned that their employees are not burning out by beings always on. - Good collaboration tools - **Chat tool:** All employees -- at all levels (even CEO) -- need to be online, and active in a company wide messaging. This is your legs when you need to stop by someone’s desk to ask a quick question. Or this is a place where coffee station discussion turns into an extended brainstorm or technical discussion with decisions. I am not particular to any one chat tool as long as I can search chat archives for previous discussion points. - **Video conferencing:** We are human and we thrive on contact. Whenever possible, meetings (even this customers) should be done with live video. We gain a lot of insight be reading the body language of others. This also forces you to get out of your pajamas and dress for work. ;-) I do like Google Hangouts because it is integrated into my Google calendaring system. - **Shared document system:** A tool like Google docs. Desktop editing systems just do not work for distributed teams. You collaborate in the documents. There are some obvious omissions from this list of remote characteristics, such as great work ethics, reliability, ability to manage your time, and of course Trust. These are very, very important, but these are very, very important for any well functioning team and business. ### How did remote work change or impact your life? If you have ever read one of the original career management books, “What color is your Parachute”, Richard Bolles discusses the principal around your career success and happiness starts with the foundation of being happy with where you live. You spend so much time outside of work, so everything grows from that base. So, for me, remote management has allowed me to be flexible in where I can live. So, now I live in the Colorado Rocky Mountains a resort town that features world class skiing in the winter, a world class river in the summer, open wilderness, and also another love, many mountain biking trails. ### Other than your home base, what is your favorite place to work remotely from? With age I have lost a lot of near field vision. So, I am not nearly as productive away from my home setup. I also need large screens to spread out my work across my monitors (desk). So, while I do enjoy the occasional coffee shop to get away. I only do that if I am reviewing documents and responding to emails that don’t require a lot of analysis. ### What are some habits or rituals that you do to keeps you going? For me it is ensuring a regular schedule of biking and skiing. For others this may just be their favorite hobby. We need something to help us refresh. Also, ensuring you get out of your home to interface with others. ### Can you describe your home setup? I have a sit/stand desk. It is of decent size. It has a manual control that allows me to very quickly adjust height. I have a Herman Miller ergo chair for when sitting and a foot pad for when standing. I also have my desk next to a large window that overlooks the mountains. I have a 15” laptop that is connected to a docking station connected to a large monitor, external ergo friendly keyboard and mouse. I only use a single monitor, but do use my laptop screen as a second display. ### What are your favorite tools for collaboration? - Google suite of tools (Google Docs, Hangout, Calendar) - Telegram ### What advice would you give to someone who has never worked remotely before and is about to join a remote company? Have a routine and a schedule. Don’t let the fact that work is always “here” get in the way of the things you like away from work. This doesn’t mean you don’t absolutely love the work you are doing. It is more that you also need to get away from “work” to refresh your mind and help you be more productive at your work. ### At Canonical, the teams often do in-person sprints and summits. How do you think these gatherings impact team spirit and productivity? These meetings are critical to spirit and productivity. I suspect the brick and mortar businesses would also find the same as these meetings force the process to "stop" and "confirm" your directions are still valid, and provides a good "pivot" point as required. These meetings also build connection points between the business and technical areas. All business and technical owners are present; thus allowing for quick iterations and not getting bogged down. This process builds strong relationships and connections, and an understanding around the important linkages and dependencies that otherwise take a longer time to build. This results in building strong relations even among teams that may on the surface appear to be unrelated and disjoint. ### What is a good frequency for these in-person gatherings? Canonical does these on a quarterly basis for business roadmaps, and semi-annually for engineering teams. There are some people who need to attend both, so that is 6 times a year for a select few -- and that does get rather extensive so things may change in the future, however right now we have a number of strategies in flight so the increased frequency is currently necessary. ### At these summits, what does the agenda look like? They are different depending upon an engineering versus a business summit. Business reviews have a combination of roadmap presentations from the different business units, breakouts for deep dive presentations that are open to any attendee. Everyday we close with "lightening talks", which is a series of 5 (10 max) minute presentation of a topic of interest -- these vary widely. The engineering sprints typically have very short 30 minute intro for the day, and then each engineering team have own deep dive rooms. Agendas vary based upon the team. These are more of individual engineering teams co-locating their individual summits at the same place and time to work on very specific technical issues with advantage of other teams being present as required. These meetings also close out the day with "lightening talks". ## Looking to learn more? While you're here, I'm thinking about writing a book on this topic. [Sign up here](http://eepurl.com/gtzNfb) for updates. --- # Building a Pwnagotchi for WiFi penetration testing (with a PaPiRus Zero display) URL: https://vpetersson.com/2019/11/21/building-a-pwnagotchi-with-papirus/ Date: 2019-11-21 ![](https://media.giphy.com/media/MM0Jrc8BHKx3y/source.gif) Security has been an interest for me for a long time. This is why [Pwnagotchi](https://pwnagotchi.ai/) piqued my interest. Using cheap hardware, you can create your own lightweight WiFi (and Bluetooth) sniffing device. Thanks to a known vulnerability in the WPA/WPA2 protocol, the Pwnagotchi can [capture the handshake](https://pwnagotchi.ai/intro/#wifi-handshakes-101), which we can then use to crack the passphrase (more on that later). My personal interest in this was largely to test my own OpSec to ensure my own WiFi wasn't vulnerable to simple attacks like this. Fortunately, it wasn't. So what do you need to build your own Pwnagotchi? While it support multiple hardware, I used the following to build mine: ![](/assets/pwnagotchi-parts.webp) - A Raspberry Pi Zero W - **Please note that you will not be able to sniff 5Ghz WiFi with the Raspberry Pi Zero W**, as it only support 2.4Ghz. To sniff 5Ghz WiFi, you need to either use a Raspberry Pi 3 Model B+/4 Model B (which draws much more power) or potentially use a USB WiFi adaptor. - An SD card - A PaPiRus Zero - You probably want to get a [Waveshare](https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT) one instead, as this is the officially suported display - A PaPiRus Zero Case - A battery pack (most will do just fine) - Two Micro USB cables - Technically speaking, you only need one cable as you can power the device from the data port. However, having two cables allow you to drive say the Raspberry Pi from a battery pack and still read data from it without having to shut it down. Once you have all your parts, simply go ahead and [flash out the disk image](https://pwnagotchi.ai/installation/#flashing-an-image) to your SD card. Since we are using a PaPiRus display, we need to make some [configuration changes](https://pwnagotchi.ai/configuration/). The configuration file will look something like this: ``` main: name: 'pwnagotchi' whitelist: - 'YourHomeNetworkMaybe' plugins: grid: enabled: true report: true exclude: - 'YourHomeNetworkMaybe' ui: display: type: 'papirus' color: 'black' ``` With this done, you should now have your own Pwnagotchi up and running and start showing data on the screen. ![](/assets/pwnagotchi-running.webp) Please note that you should leave your device up and running for at least 30 minutes during the first boot for it to get properly configured, as it needs to do a fair bit of processing. You can see the progress of the Pwnagotchi on the screen. Once it has captured some handshakes, you need to copy the files from the Raspberry Pi for processing, as the Raspberry Pi is nowhere near powerful enough for this. This is why we need our second MicroUSB cable. There are instructions [here](https://pwnagotchi.ai/configuration/#connect-to-your-pwnagotchi) on how to do this. With a data connection to your Pwnagotchi established, you should be able to see the `.pcap` files under `/root/handshakes/`. Copy these files to your computer (ideally one with a powerful GPU). We now need to first convert the `.pcap` file(s) to a `.hccapx` file(s), as well as downloading the dictionary we want to use to crack the WPA/WPA2 key. You can find details on how to do that [here](https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2). (`hashcat` is available from [Homebrew](https://brew.sh/) on macOS.) If you've followed the instructions, you should be able to do this: ``` $ hashcat -m 2500 foobar.hccapx rockyou.txt [...] ``` Depending on your hardware, this will take 30 minutes to hours. At the end of the result, if you're lucky (or unlucky), you will see your WiFi passphrase listed. While we've only covered how to capture WPA/WPA2 handshakes in this article, Pwnagotchi is capable of even more. You can read more about that [here](https://pwnagotchi.ai/usage/). Happy hacking! --- # Home Assistant, ESPHome and JZK ESP-32S URL: https://vpetersson.com/2019/11/16/home-assistant-and-esphome/ Date: 2019-11-16 Long story short, I've pimped out my apartment with a lot of Ikea Smart products, such that I can control (and automate) everything from [Home Assistant](https://www.home-assistant.io/). While I admittedly have a love-hate relationship with Home Assistant, it is generally speaking a pretty impressive software. One thing I've been meaning to do for some time is to log the temperature in various rooms. Since I didn't want to put a Raspberry Pi in every room, I opted for an ESP32 with a DH22 sensor. While I initially planned to write a simple web server or MQTT client to export the data, I was lucky to run across [ESPHome](https://esphome.io/), which does all of this out of the box. Moreover, it also integrates seamlessly with Home Assistant. As someone who haven't spent a ton of time with embedded hardware, it took me a little while to get this all working. Most of the time however was spent trying to get my [JZK ESP-32S ESP32](https://www.amazon.co.uk/gp/product/B071JR9WS9/) to properly read the sensor. Due to very poor documentation for the board, I wasted a lot of time. However, after finding [these schematics](https://duino4projects.com/esp32-devkit-v1-pinout/) I was able to get it to work on D25. ![](/assets/home-assistant-and-eshome.webp) For those who bought the JZK ESP-32S ESP32, here is the configuration that I ended up for in ESPHome: ``` esphome: name: office platform: ESP32 board: esp32doit-devkit-v1 wifi: ssid: "not-my" password: "wifi-confi" # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "Office Fallback Hotspot" password: "xxxx" captive_portal: # Enable logging logger: sensor: - platform: dht pin: 25 model: DHT22 temperature: name: "Office Temperature" humidity: name: "Office Humidity" update_interval: 60s # Enable Home Assistant API api: ota: ``` Other pro-tip for people new to Home Assistant: - Don't use a Raspberry Pi if you're doing something beyond very basic. Instead use a virtual machine or similar. - [Switch to using MySQL](https://www.home-assistant.io/integrations/recorder/) instead of the default Sqlite3 database as the backend as it will significantly improve performance. - **Bonus:** Adopt [InfluxDB](https://www.home-assistant.io/integrations/influxdb/), which you can then consume from [Grafana](https://grafana.com/) for better visualization. ## Update 1: Air quality monitor Since starting working this, I've modified the board to also include an air quality sensor ([CJMCU-811](https://www.amazon.co.uk/gp/product/B07RGLMS1J/)). I've also gone ahead and published [the schematics](https://easyeda.com/vpetersson/room-sensor). The next step is to solder this together on a smaller breadboard to reduce the footprint. ## Update 2: Transferred to a proper board Today I finally received my breadboard and transferred the components. Looking pretty sleek. ![](/assets/room-sensor.webp) --- # Install Ubuntu Core 18/22 on Proxmox URL: https://vpetersson.com/2019/10/03/ubuntu-core-on-proxmox/ Date: 2019-10-03 Today I was trying to get Ubuntu Core 18/22 working on Proxmox. Given that it is a KVM based tool, it's fairly straight-forward, but took me a bit of time go get working (thus this write-up). There are good [installation instruction](https://ubuntu.com/download/iot/kvm) for how to install Ubuntu Core on KVM, but I needed to do a little bit of work got it running on Proxmox. Before we begin, go ahead and create a VM in Proxmox's interface. Make sure to do the following selections: - Select 'Do not use any media' for the installation drive - Set the BIOS to 'OVMF (UEFI)' - Remove the default disk that is added (we'll import one later) - Set the CPU/RAM configuration as per your desired, but one core and 512MB RAM should be plenty Once done, take note of the VM ID (as we'll need that below) and what pool (you can use `pvesm status` to list your pools) you want to use. With this information, it's now time to SSH into the server and download the disk image and import it Proxmox. ``` $ wget http://cdimage.ubuntu.com/ubuntu-core/18/stable/current/ubuntu-core-18-amd64.img.xz $ unxz ubuntu-core-18-amd64.img.xz $ qm importdisk $VM-ID ubuntu-core-18-amd64.img $DRIVE-POOL $ rm ubuntu-core-18-amd64.img ``` Assuming everything went well, the drive should now be showing up as 'Unused Disk 0' under the VM. Attach the drive as a `virtio` (to get the best performance). Before booting the VM, go to 'Options' -> 'Boot Order' to ensure that the harddrive is set to 'enabled' and has a boot order. You may also want go to 'Hardware' and add a 'TPM State' (v2) if you want to also use Secure Boot (and possibly even Full Disk Encryption). With this done, you should now be able to boot up your Ubuntu Core VM and be on your way. --- # Interview with Rimas Mocevicius from JFrog on working remotely URL: https://vpetersson.com/remote-work/2019/09/20/interview-with-rimas-mocevicius/ Date: 2019-09-20 First out in this interview series, we have [Rimas Mocevicius](https://twitter.com/rimusz). Rimas is someone that I got to know from the London Kubernetes scene some time ago. He worked on Deis (before it got acquired by Microsoft) and is the co-creator of the popular Kubernetes packaging tool [Helm](https://helm.sh/). These days, Rimas works at JFrog and on their various Kubernetes tooling. In this series, I am intending to interview people who work remotely about their habits and what they have learned over the years both about themselves and about what works and what doesn’t work in remote teams. I aspire to both interview individual contributors, as well as team leaders. Without further ado, here is Rimas. ### Can you tell me a bit more about yourself? I am first and foremost a big open source fan. I like good food, coffee, tea and of course and I cannot say no to cheesecake and halva. I have a love for travels (both for work and pleasure). ### How long have you been working remotely? I have now been working remotely, for over four years. ### Where are you based out of? I’m currently based out of London, UK (a.k.a. Brexit nation) ### Can you describe your home setup? - A large standing desk that can be adjusted for either standing or sitting. it allows chose when to sit or stand. - A big external screen for the MacBook, external keyboard and trackpad A setup like this provides a great work space both in terms of ergonomics and productivity. By also having a docked laptop, it allows for easy travels, where you simply just bring the laptop without having to worry about syncing data. ### What are some characteristics of a well functioning remote team? Very good communication is key in a distributed team. It is important that everyone is on the same page, such that they can work on the relevant tasks in an asynchronous fashion. In my case it is done mostly through Slack, with daily stand-ups as well as weekly meetings ### How did remote work change or impact your life? There are many benefits with working remotely. Not having to commute to work is a big plus, as it allows me to save a lot of time. It also allows me to spend more time with my family when I’m not traveling. Another perk is the ability to have flexible work hours, such that I can more efficiently schedule my days. Other than your home base, what is your favorite place to work remotely from? I don’t have a favorite place to work remotely from, but I do end up traveling a lot. Much of my work is done on trains, airports and hotels, and in some cases on a beach somewhere. ### What are some habits or rituals that you do to keeps you going? One of the most important habits would be to always start and finish work at the same time, just like if you were going to a regular office. Without this, it’s easy to blur the line between work and personal time. It’s also important to take breaks for tea/coffee and lunch. When working from home, it’s easy to just keep going all day, but the reality is you need those breaks to perform well. For me, it’s important to exercise before work. It could be something simple like going for a walk in the morning. I also like to round off the day with a walk after work day too if possible. It’s also important to not burn yourself out. If you have to stay late and work across time zones or for a deadline, it’s important to start a bit later the next day. If not, you’re going to create burn out eventually. ### What are your favorite tools for collaboration? My favorite tools right now include Slack, plain old email, Zoom, Git, and shared google docs. ## Looking to learn more? While you're here, I'm thinking about writing a book on this topic. [Sign up here](http://eepurl.com/gtzNfb) for updates. --- # What are the best productivity hacks? URL: https://vpetersson.com/remote-work/2019/07/17/best-productivity-hack/ Date: 2019-07-17 People love talking about productivity hacks (a.k.a. productivity porn). The "hacks" usually range from the latest tools (todo-lists, email clients) to workflow improvements. As someone who have paid attention to this for the last decade, and spent far too much time experimenting with these "hacks." Today, I can say one thing with confidence: Unless you get the basics right, these hacks won't matter. They might marginally improve your productivity, but they won't get you the productivity boost you're looking for. So what's the secret to high productivity? **drumroll** - Get your sleep - Exercise regularly - Eat healthy - **Bonus:** Cut out alcohol If you get these things right, the rest will follow. The mental clarity from a good night's sleep with multiple hours of deep sleep will run circles around all the other productivity hacks you're experimenting with. I know that it's not what you were looking for. Everyone wants a magic pill or tip that allows them to have the cake and eat it too. --- # So you want to build a remote company? URL: https://vpetersson.com/remote-work/2019/07/14/so-you-want-to-build-a-remote-company/ Date: 2019-07-14 Little did I know when I wrote [A Decade of Remote Work](/2019/05/18/a-decade-of-remote/) that it would completely blow up. After being featured on Hacker News, the article to this day keeps driving a significant amount of traffic from all kind of sources (including Twitter). Since the article, I’ve started a [series of articles](/remote-work/) on remote work. In this article, I will explore the topic of creating a remote company. I briefly touched on this topic in the initial article, but in this article, I will explore this in greater detail and include things like how to setup a company, to how to hire and what tools I personally I find necessary to get off the ground (including the reasons for these tools). Please note that the advice below is largely focused around software companies, and it will likely not be very useful for non-digital companies. ## Incorporation Many first-time entrepreneurs find the process of creating a company a daunting task. It’s understandable. Depending on your jurisdiction, the complexity of starting a company varies. This of course is added on top of the mountain of things that a first-time entrepreneur needs to be get familiarized with (such as product development, potential fund raising, hiring etc). While [IANAL](https://en.m.wikipedia.org/wiki/IANAL), my non-legal advice would be to try to keep things simple. If you expect your customers to be based in the US, it makes sense for your company to be incorporated in the US. If your customers are expected to be based in the EU, then incorporating in EU makes sense. Note that I haven’t mentioned at all where _you_ are based. Having run companies in multiple countries by now, I can say that operating headache of a company in the US is a lot lower than it is in the EU (Sweden was a major PITA). I have no personal experience of running companies in Asia, but presumably Hong Kong and Singapore are good places. Depending on where you decide to incorporate, I would recommend [Stripe Atlas](https://www.stripe.com/atlas) for US companies, and [Startup Estonia](https://www.startupestonia.ee/) for EU companies. It should however be said that I have yet not used any of these two services myself, but I hear good things about them. Also do note that if for instance you are looking to tap into a government scheme for fundraising/financing, such as SEIS in the UK, you likely need to be incorporated in the given country. The bottom line is that try to keep things simple for both yourself and your customers. Contrary to the common advice you will receive from your local advisors, this does not mean that you’re stuck to incorporating in the country you’re from/live in. If anything, this likely makes you _less_ attractive to future investors/buyers than if you’re incorporated as say a Delaware C-Corp (which is the golden standard for tech startups). ## Tools for collaboration Ok, so you have now incorporated and are ready to start building your company. The first thing you need is to get the tools ready such that your team can start build the product effectively. While I will try to avoid recommend any particular tool, and rather give categories of tools that I find required for day-to-day activity of a software company. These categories of generally look something like this: - External communication (i.e. email, these platforms usually come with calendar as well) - Internal communication (such as Slack for text based, and some other video conferencing app) - Code hosting (such as Github or Gitlab) - Project management (Github now does a good-enough job for this these days IMHO) - Design review tool (this is often overlooked, but tools like [InVision](https://www.invisionapp.com/) makes a massive difference when reviewing product and website design) Finally, don’t get tempted to run things yourself. **Go for hosted solutions**. While it might seem tempting to run service X yourself, because “_a VM on Digital Ocean only cost $Y per month, while the hosted version cost $Z per month_” (where Z>Y). Unless you’re operating at **very large** scale, this rarely ever makes sense. Thing break, and you **will** end up spending a lot of time maintaining said server(s), have downtime, and/or forget to take proper backups, which will lead to data and productivity loss. ## Hiring I briefly touched on hiring in my initial blog post. It’s a topic worth exploring further, as it is far from trivial and _will_ consume a huge part of your time as an entrepreneur. Let’s start with sourcing. While sites like Upwork has received a bad rap because of the poor quality of the majority of the candidates, it is still one of the best places to recruiting talent. Yes, you will receive a large number of unqualified applicants, but there are ways to work around this. The reason why I personally like Upwork is because of the massive talent pool. In recent years, there has been a big increase of job boards that focuses on remote workers. However, they tend to just be more expensive and more polished versions of Upwork (don’t get me started on Upwork’s horrible and insanely buggy user interface) with smaller talent pools. So, used right, Upwork can be used to find qualified team members. Here are my tips for when hiring on Upwork: - Ask questions where applicants are required to write a proper answer. The key here is to rule out applicants that simply applies to a large number of roles without effort. - Cover letters are overrated. A set of well-crafted questions will give you a lot more insight into the candidate than a cover letter where the candidate raves about how amazing your company is (and where 90% of the content is re-used). - Sneak in a test in the job description to test for attention to details. This can take a lot of forms, but for instance, you could say that they need to include a certain word in their application, or apply using a special process. - Look at their Github profile and/or portfolio. Their prior work usually tell you a good amount about a person. Do however note that there are great applicants that have little to show for their work because they ware not allowed to share it due to their previous employer. - You get what you pay for. Yes, pricing arbitrage is still a real thing, but you’d be foolish if you assume that a talented developer in say Russia will not sooner or later realize his/her market rate. You can still find great talent at a large discount compared to your local market, but don’t expect a developer that cost $8/h in India to be comparable to a developer that cost $30/h in Russia. Global markets like Upworks help establish fairly sensible equilibriums between quality and price (in particular with the review process). - Never hire contractors affiliated with agencies. I outlined why in the initial blog post, but in short, you want a direct relationship without a proxy being involved. The above tips, combined with ruthless screening should help you find those diamonds in a rough that are still out there on Upwork. ## Managing the team You’ve now incorporated, set up the required tools for collaboration, and have hired your first N team members. Good job! After granted everyone access to the various tools. Let the fun begin. One of the most important thing to do is to make your team members feel like they are a team, and not just a cogs in a wheel. From my experience, many Upwork contractors are used to being treated like crap and to just blindly take orders (usually from people without domain expertise). This is where you have to differentiate yourself to get the best out of your team. Make them feel part of the process. Listen to their input matter. It usually takes a while for them to feel comfortable to open up and have an honest discussion with you, as they’re used to just receiving orders, but it’s essential for a well-oiled team. While there are many things that are different when running a remote team, most of the core management principals still apply. For instance, feedback is still important (praise in public, criticize in private etc). One thing that is different when managing a remote team has to do with the asynchronous nature of the work. Because the team members are likely on different timezones, it is essential to have good processes for managing work. In the collaborative section, I mentioned a product management tool being essential. This should of course not come as a surprise, but it is important that each team member has a backlog that can be worked on (either individual or as part of their team). There are numerous process for managing this, where Agile/Scrum is likely the most popular way of doing this right now. How you do planning/review/retrospectives is beyond the scope of this article, but the what is essential is that every team member can simply wake up in the morning and know what they are expected to work. It is also important that the workload is predictable and priorities are not changing every day (or worse, multiple times per day). Lastly, you need to trust your team members. I wrote about this in [How we work at Screenly](https://www.screenly.io/blog/2016/11/23/how-we-work-at-screenly/). In short, if you don't trust your team members, and you feel the need to constantly monitor and micro manage them, you're fighting a losing battle. Nobody wants to work for someone who doesn't trust them. The above is by no means everything you need to know to manage a remote team, but it should hopefully get you started. If you got any questions, or additional tips, feel free to tweet me at [@vpetersson](https://www.twitter.com/vpetersson). --- # Remote Work is Deep Work URL: https://vpetersson.com/remote-work/2019/06/24/remote-work-is-deep-work/ Date: 2019-06-24 Over the last few years, two things that I’ve paid close attention to are Deep Work and Remote Work. In this article we will explore this and discover how closely these to concepts overlap. Back in 2009, Paul Graham, wrote an essay titled [Maker’s schedule, Manager’s schedule](http://www.paulgraham.com/makersschedule.html). In the essay, PG outlines the vast difference between how a manager’s schedule look like compared to how a developer (or maker) schedule looks like. PG writes that a single meeting can sometimes affect his productivity for the entire day. I too can relate to this, and have many times felt the same way. I find that if I have a meeting in the afternoon, I’m far less likely to sit down in the morning to do some focused work, compared to if I have a clear calendar. What PG is describing is really Deep Work, something that Cal Newport popularized in his book by the same name. Newport outlines the importance of cutting out noise and building good habits (I wrote more about my habits in [A Decade of Remote Work](/2019/05/18/a-decade-of-remote/)). Reading Deep Work was somewhat of a game changer for me personally, as it reinforced many concepts that I’ve had found working in my own life, such as: - Using the Pomodorro Technique - Batching email twice per day - Clustering meetings for one or two days per week Newport backs this up with both anecdotal evidence of how some of the greatest thinkers in history worked as well as with scientific findings. Before we dive into how Remote Work can be somewhat of a requirement for Deep Work, let’s take a step back and explore why Deep Work is needed and for _whom_ it is needed. As PG points out, the “Manager’s schedule” is based on the fact that a single task is will not take more than a few hours at most, and that most of the time is spent in meetings (usually booked with hourly slots). This is the opposite of Deep Work. Deep Work would be if you’re working on some complex programming task or perhaps a writing project where you need many hours of uninterrupted focus. The _content switching cost_ is extremely high. A single interruption from coworker knocking on your shoulder asking you for some piece of information or to grab a coffee can literally set you back an hour or two just to regain that flow and focus. (It is not a coincident why so many developers are happy to spend a few hundred bucks on a pair of good noise cancelling headphones when they are forced to work in an open office environment.) This is why the office environment is kryptonite for focussed work (a.k.a. Deep Work). An office environment makes it _too easy_ for peers (be your coworkers, your boss, or your boss’s boss) to just walk over to your desk and ask for something. This, of course, gets even worse in an office with an open floor plan. This leads us to the point why Remote Work is Deep Work. What’s the best way to prevent your coworkers to walk over and tap you on the shoulder? Well, if you work remotely, unless you happen to live neighbor with coworker, the probability of them walking over to you and knock on your door is close to nill. This is why I would argue that Remote Work is the best way to do Deep Work. If you want a stretch of six hours of uninterrupted work, all you need to do is to turn off your notifications. Traditional managers first response to when they hear about Deep Work is to immediately retaliate with a comment about how they need to be able to get ahold of anyone immediately if there is an emergency. This is a natural reaction, but it is flawed. The reality is that very few “emergencies” are actually that urgent. They can usually wait a few hours. Moreover, if you have a company where you are constantly fighting emergencies, you have a far bigger problem on your hands. That’s a symptom of a highly dysfunctional company, where you really should put your energy into addressing this root cause. While there are a lot of benefits with Remote Work and Deep Work, it is not a silver bullet for the entire company. There are some functions where Deep Work may be unfavorable. The functions where Deep Work would be unfavorable would be roles such as sales, where it makes sense to cram people into an open office. Sales is much like the Manager’s schedule, where you have blocks of hours with little need for long stretches of focused work. Sales also tend to attract extroverts, and having them in the same room, where they essentially compete against each other with leaderboards can help drive the overall performance. I hope you found this useful. If you want to learn more about remote work, sign up to my [Remote Work newsletter](http://eepurl.com/gtzNfb). Also, you might find more articles on remote work [here](/remote-work/). Kudos to my good friend [Philip](https://twitter.com/philfortuna) for the inspiration for this article and pointing out the link between PG’s post and Deep Work. --- # Mental Health and Remote Work URL: https://vpetersson.com/remote-work/2019/06/19/remote-and-mental-health/ Date: 2019-06-19 In recent years, we’ve started to see more people speaking out about mental health in the tech industry. This is great, as it is a topic that has been somewhat taboo in the past. In my last blog post, [A decade of remote work](/2019/05/18/a-decade-of-remote/), I did not explicitly speak about mental health, and I must admit that it is something that I’ve only recently started to pay proper attention to. I would however argue that mental health does overlap with habits (which I did cover). I don’t think it is possible to have good mental health without good habits. If we for a second assume that remote work equals working from home (it does not necessarily need to mean that), then it isn’t very hard to imagine that it having an impact on your mental health. Spending 8-10 hours in solitude every day will impact people differently. If you’re an extrovert, this might have an impact your mental health in a negative way (unless you have a good coping strategy). If you’re an introvert on the other hand, having that much time without having to deal with people around you, might have a big positive impact on your mental health. Regardless if you are an introvert or extrovert, it is important to carve out time in your schedule to socialize. If you fail to do this, chances are that your mental health will take a toll with time and you will start feeling isolated and lonely. Loneliness is widespread phenomena in the western world ([Bowling Alone](http://bowlingalone.com) by Robert D. Putnam provides some clues to why). The reality is that while there are many downsides with working in a conventional office, one of the upsides is that you get circle of acquaintances that you see regularly. In some cultures, such as in England, it is expected that you go out for a pint with your colleagues after work. The point I’m trying to make is that, if you’re working remotely, you’re on your own. You are left to your own devices to find a substitute for the casual social activities. This is, of course, not a substitute for deeper, meaningful relationships, but it all plays a role in the mental health. Let’s imagine that you’re new in town and you are looking to find a substitute for these casual social encounters (which of course could lead to more meaningful and deeper friendships with time), what do you do? As someone who has lived in multiple countries and been in this precise situation myself numerous times, one of my best advice is Meetups. The Meetup scene will vary a lot across cities, but in cities such as San Francisco and London, it’s thriving and you can usually find a Meetup that aligns with your interest, regardless if that’s a technical topic, bio hacking, or urban farming. Over the years, I’ve found many friends this way. If you’re not (arguably) lucky enough to live in a major city, there are usually countless of other groups that you could join (such as running, triathlon, scuba diving or biking). While having casual social gatherings, this is not a substitute for your closest friends and loved ones. Depending on your situation, you may now be living far away from your old close friends and family. If that is the case, it is easy to get caught up with work and forget to carve out time in your calendar for them. One way to do this is to simply block time in the calendar for this activity. Maybe you schedule a biweekly FaceTime call with your old friend to ensure you don’t lose touch. Things like this can go a long way with regards to your mental health. I would also note that if you’re living in a busy city environment and your have close friends locally, it is easy to fall apart. Everyone is busy, you will need to book things properly in your calendar to catch up. If you do not, chances are that your relationships will deteriorate with time. The grass is greener where you water it, as my psychiatrist keeps saying. Having healthy relationships with your friends is important, but for many people, the most important relationship is with their significant other. If you work from home, and your significant other is working from an office, there will likely be somewhat of an asymmetry when s/he comes back from work. While you might be excited to finally see a person when s/he gets home, s/he might be exhausted and just want peace and quiet. Be mindful of this, as it could otherwise overwhelm your significant other and have a negative impact on your relationship. ## Closing notes Take care of your mental health and carve out time to stay in touch with your friends and family. Mental health is something that we easily overlook in our busy lives. If you do not feel well mentally, there is no shame in seeking out professional help. There are a number of professionals who even offer remote sessions. ## Looking to learn more? While you're here, I'm thinking about writing a book on the topic of remote work. [Sign up here](http://eepurl.com/gtzNfb) for updates. --- # A Decade of Remote Work URL: https://vpetersson.com/remote-work/2019/05/18/a-decade-of-remote/ Date: 2019-05-18 ## Intro While still in college (go [Broncos](https://www.santaclarabroncos.com)!), I teamed up with Alex ([@slevenbits](https://mobile.twitter.com/slevenbits)) to create a startup. We were young, inexperienced and naive. Our first project was called YippieMail and it was an email aggregator. Simply put, YippieMail could display all your webmail accounts (i.e. Hotmail, Yahoo and Gmail etc) in the same web interface (this was before most email providers supported IMAP, so you couldn't use an email client). Looking back at it, YippieMail was a pretty stupid idea, but it did land us meetings with Sequoia Capital and few other VCs on Sand Hill Road. Keep in mind that this was around the time [Meebo](https://en.m.wikipedia.org/wiki/Meebo) raised many millions from Sequia and DFJ to do the same thing but for Instant Messaging (IM), so at the time it probably did not seem as such of a bad idea. It was in the early days of YippieMail, which was pivoted into [YippieMove](https://www.yippiemove.com) (RIP 2008-2019) my now decade-long remote experience began (some of which as a digital nomad). When we began working on YippieMail, Alex was living in San Jose, and I was living in Mountain View. For those of you not familiar with the Bay Area, these two cities are not very far apart, but when you factor in the horrendous traffic conditions, it can easily take well over an hour to drive between the two (while it might only take 20-30 minutes without traffic). It was then we decided to work remotely rather than getting an office somewhere in between. Ever since, in all our subsequent ventures ([Blotter](https://www.wireload.net/products/blotter/), and then [Screenly](https://www.screenly.io)) have been remote-only. To this day, even though both Alex and I both live in London, we only get together every other month or so in person to catch up. So what have I learned over this decade of working and running remote teams? Let’s dive in. ## Remote is not for everyone The first thing that I would like to point out is that remote working is not for everyone. Over the years, we have had a few team members that could not work remotely. In some cases these people discovered this themselves and chose to leave, and in some other cases it became clear that it was not a match. Usually, people who fail at remote work tend to either lack the self-discipline it requires, or they are simply socially oriented and thrive being around other people. In the latter case, working from a shared office can help, but even then, if you lack the self-discipline and habits required, you are likely not going to thrive. While there are plenty of exceptions to this rule, young people (early 20s) tend to struggle more with this than people who have reach their late 20s and early 30s. The bottom line is that some people excel while working remotely, while others work better in a regular office environment. It’s hard to screen for this in an interview, but it usually becomes evident during the first year. It is important to look out for this in team members as a manager. ## Either you’re remote or you’re not Either you’re remote-only or you don’t do remote at all. Lots of companies brag about giving their staff the freedom to work remotely. However, the reality is that unless it is in your company's DNA to be a remote company, it will inevitably favor the team members that are working in the office (in particular if this is where the leadership is). The reason for this is largely related to the flow of information. People chat over the water cooler, over coffee or over drinks after work. This leads to unevenly distributed information, which easily can make people feel left out or that other team members simply assume everyone else knows about something despite it never made it to the official channels. In a remote-only culture however, the information flow tends to happen in a more organized fashion either over email or in the company chat rooms (or even in GitHub Issues). ## Company and team summits Company summits matter a lot. Even if you’re a remote team, having everyone get together in person every year (or twice a year) can make a huge difference. While video chats is a [higher context](https://en.m.wikipedia.org/wiki/High-context_and_low-context_cultures) medium than email or chat, it still isn’t a full substitute for meeting face-to-face. When we did our very first summit for Screenly at the lovely [Villa Lava](https://www.villalavacroatia.com) in Croatia (a great place for company summits), it was the first time our team members got to meet each other in person, despite having worked together for years. In retrospect, it was a big mistake to not be doing summits earlier, as we could see a big difference in _how_ the communication changed online _after_ the summit. Because text based chat is a low context medium, it’s very easy to misread the intent of a message. However, if you have met this person in real life, you have a lot more to work with and can use that context to read the same message in a new light. These days, the developers at Screenly get together in person every quarter (roughly) for a one week summit. The entire company gets together annually. (You can read more about how we work at Screenly in the article [How we work at Screenly](https://www.screenly.io/blog/2016/11/23/how-we-work-at-screenly/) that I wrote a few years ago.) ## Tap in to a large talent pool Hiring remote means a larger talent pool. I’m hardly the first to point this out, but one of the major reasons why it makes sense to be remote-only. You are no longer limited to hiring in your geographic area. The tools for recruiting have changed a lot over the last decade since I started working remotely. That said, recruiting is far from easy. Because people from around the world are able to apply to your openings, the second you post a job ad, the floodgate opens. The reality is that 99.9% of the applicants for remote jobs are people who utilize the “spray and pray” approach. Filtering them out is fairly easy, but in best case scenario, you’ll have a handful of decent candidates for every 100 or so applicants. The filtering process that tends to work well is to have rigorous screening questions that actually requires a little bit of work and is unique. This will help you weed out all the candidates who simply put “Call me to discuss” in all the boxes (or worse). Yes, this screening process will take a fair bit of time, but tools like Upwork make it fairly quick to reject candidates that fail to put in the effort (or are clearly poor fits). From experience I am also very reluctant to work with agencies and prefer hiring team members directly. The reason being that a number of agencies we've run across over the years have a small amount of talented engineers that will do the screening process and perhaps the first few weeks, and then they gradually shift the work over to a more junior person, while charging the same rate. It's also worth mentioning that with the raise of the digital nomad movement, there are a lot more job boards that are "remote friendly," including [Angelist](https://angel.co) and a plethora of (IMHO overpriced) remote-focused job boards. A final word of warning for people hiring remote team members: don't hire people who want to join your company _just_ because you offer them to work remotely and subsequently have more flexibility. While not always true, it is sometimes an indicator of people who want to coast along with minimal supervision (while perhaps getting their own business off the ground). What you really want is people who believe in the vision and product, and where remote is a perk, not a the reason why they want to join. (I have intentionally not mentioned the legal structure of how to hire remote talent. IANAL so you probably should check with one to ensure you comply with the local laws.) ## Finding good remote workers is probably easier for some roles than others Remote work is likely easier for engineering than for other roles. In all my experiences, we have always been engineering heavy organizations. Yes, we’ve had a number of other roles too, but in terms of head count, the engineers always outnumbered all other roles. What I have noticed however is that it tends to be easier (in general) to manage engineers remotely compared to other roles (such as sales). This is likely related to a number of variables, but in general, I’ve found engineers to be more self-motivated and requiring less handholding. There is of course a large correlation with seniority too. Regardless of position, more senior people tend to require less handholding and thus work better remotely. ## Remote is a major time saver Remote work saves a lot of time. First, it should be said that remote work does not necessarily equal working from home. We’ve had plenty of team members over the years that preferred to work from a shared office (including myself for a period). To each and their own. If however you work from home, you can save a big chunk of time (and money) every day. When I had an office in Shoreditch, it took me 30-40 minutes each way. That adds up top a lot of time every week. These days I have a dedicated room as my home office (something I _strongly_ recommend if working from home). This means that my morning commute is roughly 60 seconds, and that includes a detour to the kitchen to fetch myself a cup of joe. What you do with this time is up to you, but I usually dedicate this 1-1.5h every day to exercise. ## The power of routines and habits Habits will make or break you as a remote worker. As mentioned earlier, remote work is not for everyone. It requires a lot more self-discipline than a regular office job where you’re constantly "supervised." Over the years, I’ve experimented with a large number of habits, and at this point I’ve devised a set of habits that work pretty well for me (but they are likely to change as I keep experimenting). **The most important habit when working remotely from home is to mentally trigger a beginning and an end of the work day**. It’s easy to sit in your PJs or sweats all day just because you can, but it will likely backfire in the long-run. To make this more concrete, here’s my current daily schedule: - 07:00: Wake up - 07:05: Reading ([Sharpen the saw](https://www.artofmanliness.com/articles/the-7-habits-sharpen-the-saw/), from The 7 Habits of Highly Effective People) - 08:00: Check in with the team - 08:10: Exercise + shower - 09:30: Start of my work day - 19:30: End of my work day - 23:00: Bedtime **Clarification**: This does not necessarily mean I work 10 hours per day every day (sometimes I do). I do take a lunch as well. Also, I am perfectly happy to wrap my day at 17:30 after a productive and successful day. The 19:30 hard stop, not as hard requirement for me to work to it every day. **Update**: As COVID-19 has taken its toll on the world, I've altered my schedule slightly. With gyms being closed, I had to replace my morning exercise (normally swimming or weight lifting) with training at home with body weight and extension bands. While I was skeptical at first to the effectiveness of this, I've grown to really like the effectiveness. As a result, I've cut down my training to about 20 minutes, and allocated the saved time towards more reading. As my good friend Milos ([@milosgajdos](https://twitter.com/milosgajdos)) pointed out while reading a draft of this post, an early start isn't for everyone. Shifting your day is perfectly fine too. The point is not when you start your day and when you wrap it, but building and sticking to habits that make you productive. If you want to learn more about the importance of habits, I strongly recommend reading [The Power of Habit](https://www.amazon.co.uk/Power-Habit-Why-What-Change/dp/1847946240/) by Charles Duhigg. Also, a word of warning, don't get obsessed with reading all about productivity. I've been a victim of productivity-porn myself, but I can tell you first-hand that you'll waste far more time reading about it than you'll ever save. ## Sleep matters (shocking, I know...) Perhaps not related to remote work itself, but more the startup culture. VC used beat it in to young and naive early 20-something kids that it was cool (and even expected) to frequently pull all-nighters and sleep under their desk. I feel like the tide has finally turned on this. Yes, you still have the Gary Vaynerchuk-wannabees out there with their hustle-porn, but I think (and hope) they are a dying breed. What is however related to remote work is the the importance of wrapping up your day. As you may have noticed above, I end my day at 19:30. After that I’m not allowed into my office (unless there’s an emergency). I also try to keep my screen time to minimal in the evenings. In my younger years, I frequently worked late into the night. Yet, even if I clocked more hours, I got less done. Switching off very is important, and it is a lot more challenging when you're working remotely. If you're struggling with switching off your devices at night, one "brute force" approach that I've used myself at times is to use one of the many parental control devices. They come in all shapes and forms (ranging from apps to hardware appliances), but what they all tend to have in common is that you can turn off your internet at a certain hour (say after 21:30). If you want to learn more on this, I recommend the book [Why We Sleep](https://www.amazon.co.uk/Why-We-Sleep-Science-Dreams/dp/0241269067) by Matthew Walker. ## Distractions kill productivity Kill the distractions. Working from home is challenging for a lot of people. It’s easy to get distracted by various things around the house, but for me the biggest distraction has always been the digital kind. Cal Newport nails this in his latest book [Digital Minimalism](https://www.amazon.co.uk/Digital-Minimalism-Living-Better-Technology/dp/0241341132), where he talks about how distracting mobile phones and social media can be. I’ve found this first hand. For a long time, I kept my phone next to me on the desk. However, every time the phone buzzed, I lost my focus. Even if I did not check the phone, it still got me distracted. The remedy for me was to simply move all my distractions to the living room (i.e. my phone and Apple Watch) and just check them periodically throughout the day. Alternatively, Airplane mode on your devices is another great way to kill noise. For the hackers out there, I’ve found that running something like [i3](https://i3wm.org/) is also great for cutting out noise on your desktop. I use this on my "developer workstation" (which is different from my other workstation). ## Don't skimp on equipment While having good equipment is always important, you tend to have more control over your equipment when working remotely than when you work in an office where everything is provided to you on your first day. You are going to spend a lot of time in front of your workstation. Your body will thank you for spending a bit more money and get: - A large 4K screen (they have crawled down a lot in price recently) on a monitor arm - A standing desk (I use [this](https://www.ikea.com/us/en/catalog/products/S29022520/) one from Ikea) - A good ergonomic keyboard ## That’s a wrap! That’s it. At least for now. I’m sure there are things that I have missed, but it should hopefully be useful for other (new and old) remote workers out there. If you are eager to learn more, I would recommend the following additional books: - [Deep Work](https://www.amazon.co.uk/Deep-Work-Focused-Success-Distracted/dp/0349411905) by Cal Newport - [ReWork](https://basecamp.com/books/rework) by Jason Fried and David Heinemeier Hansson (DHH) Also, take a look at [How We Work at Screenly](https://www.screenly.io//blog/2016/11/23/how-we-work-at-screenly/), a blog post I wrote a few years ago, but it still has relevant information. [Discuss on Hacker News](https://news.ycombinator.com/item?id=19953854). If you found this article interesting, you will probably enjoy my newer article [A decade and a half of remote work](/2025/04/07/a-decade-and-a-half-of-remote-work/). --- # Troubleshoot HTTPS with curl URL: https://vpetersson.com/2018/10/03/troubleshoot-https/ Date: 2018-10-03 Troubleshooting an HTTPS connection can be somewhat challenging at times (in particular if Cloudflare is involved). Today, I had to troubleshoot a Kubernetes (Nginx) Ingress controller that was acting up for [Screenly](https://www.screenly.io). Having done this on more than one occasion, I decided to create a public Note-to-Self in order to avoid having to Google for the exact syntax in the future. For this task, my weapon of choice is `curl`. The key we want to accomplish is to be able to explicitly specify the IP address to the load balancer in order to rule out any possible DNS issue. First, we can use `curl` to get the HEAD. The key here is that we pass on the desired domain/sub-domain using the 'HOST' header. We also specify the IP address of the load balancer directly (a.b.c.d): ``` $ curl -I \ -H "HOST: some.domain.com" \ https://a.b.c.d [...] ``` Assuming that still doesn't work, we may need to take a closer look at the SSL certificate that by adding `-w %{certs}`. Recent versions of `curl` added the `--resolve` functionality, which is handy. You can then do something like this: ``` $ curl -I \ --resolve 'some.domain.com:443:a.b.c.d' \ https://some.domain.com ``` --- # Kubernetes and RBAC with examples URL: https://vpetersson.com/2018/06/15/kubernetes-rbac/ Date: 2018-06-15 In Kubernetes 1.8, RBAC was introduced to improve the security. Prior RBAC, any pod is more or less able to interact with the rest of the cluster without constraints. This means you can create new pods, delete other deployments etc from any other pod. Needless to say, this is not ideal and RBAC sets out to address this. While RBAC is a rather complicated topic, this brief article sets out to give a very simplistic crash course to get you started (while intentionally leaving big parts out). What I wanted to do was to grant the deployment(s) the bare minimum permission to operate. For instance, if you have a simple app that neither uses Secrets or ConfigMaps, you can configure this using RBAC. Below I'll walk through two examples. One deployment that is fully locked down, and one that only has access to Secrets. Please do note that you need to have an RBAC enabled cluster for this to work. `minikube` for instance comes with this by default. ## RBAC in 15 seconds RBAC can be used for many things, but in this article we will purely focus on the runtime side of RBAC and not for cluster authentication. With that in mind, there are three main concepts that you need to keep in mind: - Role: A role is what defines the actual RBAC permission - Service Account: This is what you associate with your deployment (or similar) - RoleBinding: This is what applies the Role to the Service Account This is vastly simplified, but that should hopefully help you wrap your head around the concept. ## A fully locked down deployment (You can find the files referenced below [here](https://github.com/vpetersson/rbac-example/tree/master/locked-down-nginx).) First, we create namespace to stash our deployment into: ``` $ kubectl create ns lockdown ``` Next, we need to create a service account (which we will call 'sa-lockdown'): ``` $ kubectl create serviceaccount --namespace lockdown sa-lockdown ``` We now need to create the [role](https://github.com/vpetersson/rbac-example/blob/master/locked-down-nginx/role.yaml) that defines a fully locked down RBAC role. The role will look like this: ``` kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: lockdown rules: - apiGroups: [""] # "" indicates the core API group resources: [""] verbs: [""] ``` Simply apply the role using `kubectl create -f role.yaml`. The important part here to note is that we simply do not define any explicit rules. Next, we need to define a RoleBinding to map the Role to the Service Account we created earlier: ``` apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: rb-lockdown subjects: - kind: ServiceAccount name: sa-lockdown roleRef: kind: Role name: lockdown apiGroup: rbac.authorization.k8s.io ``` Apply the role using `kubectl create -f rolebinding.yaml`. With that applied, we can now check the permission using the `can-` feature: ``` $ kubectl auth can-i get pods \ --namespace lockdown \ --as system:serviceaccount:lockdown:sa-lockdown no ``` Lastly, we can create our Nginx deployment: ``` apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 1 template: metadata: labels: app: nginx spec: serviceAccountName: sa-lockdown containers: - name: nginx image: nginx:1.13 ports: - containerPort: 80 ``` Again, apply this using `kubectl create -f deployment.yaml`. That's it. You are now running a fully locked down Nginx container (as far as RBAC goes at least). ## RBAC with Secrets enabled Many (most?) deployments require things like ConfigMaps and Secrets. As such, we need to modify our approach slightly and grant explicit access to this. Fortunately, this is very straight forward too. All we really need to do (in addition to creating the Secrets) is to modify our role definition to look something like this: ``` kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: role-lockdown-secrets namespace: lockdown-secrets rules: - apiGroups: [""] # "" indicates the core API group resources: ["secrets"] verbs: ["get", "watch", "list"] ``` As you can see, that provides explicit permission to read secrets. Other than that the setup is the same and you can find the full example [here](https://github.com/vpetersson/rbac-example/tree/master/only-secrets). That's it. Hopefully this helped you get started with locking down your system using RBAC. --- # Sonar - A Raspberry Pi based wireless people counter URL: https://vpetersson.com/2018/03/19/sonar-databat-people-counter/ Date: 2018-03-19 Over the last year, I've been hacking on-and-off on a little project called [Sonar](https://github.com/databat-io/sonar). The project goal is to get real-world analytics for public spaces (such as retail) using wireless means at low cost. This means using Bluetooth and WiFi (the latter is to be added) and a Raspberry Pi. This weekend I finally did a brief [write-up on Hackster](https://www.hackster.io/vpetersson/sonar-wireless-foot-traffic-information-for-retail-b17cc1) where I outlined the project. Sonar is still in its early days, but you can already use it to collect BLE data from the surrounding, which can provide you with a rough estimate of the amount of people in the surrounding area. The idea is that with sufficient data collected (and with more sensors, such as WiFi), Sonar should be able to learn and provide better analytics. Moreover, with multiple Sonar devices deployed in the same venue, you could triangulate the visitors and provide things like heat maps. It should also be said that I'm well aware that there are plenty of commercial projects doing similar things, but there were surprisingly few open source projects, which is why I decided to take it on. Here are some things I have on the roadmap: - Add support for collecting metrics over WiFi - Create a proper RESTful API for report data - Sprinkle ML magic on the dataset to see what shows up :) --- # Network Performance - Raspberry Pi 3 Model B vs Raspberry Pi 3 Model B+ URL: https://vpetersson.com/2018/03/15/rapsberry-pi-model-b-vs-raspberry-pi-model-b+/ Date: 2018-03-15 Today I got my hands on the new Raspberry Pi 3 Model B+. One of the most notable new features is Gigabit Ethernet. Given that the Raspberry Pi still uses the USB 2 interface for the Ethernet controller, I was curious to see what kind of bandwidth it could handle (I know the MagPi [already published this](https://www.raspberrypi.org/magpi/raspberry-pi-3-specs-benchmarks/), but I wanted some independent numbers). To do that, I setup a simple experiment: - One Raspberry Pi 3 Model B+ - One Raspberry Pi 3 Model B - A single SD card with the latest Raspbian (that I moved between the boxes) - One Gigabit switch - A Linux box to perform the test from (wired to the network) For the test, I used the classic network testing tool `iperf`. So how did it go? Turns out that it is almost three times as fast. It is nowhere near full Gigabit speed, but it is still a nice performance improvement. ![](/assets/pi3-vs-pi3+.webp) The data is in Mbits/sec. You can find the raw numbers [here](https://gist.github.com/vpetersson/d6daa360bb207142faf6c555ff84b17f). --- # Running KVM with Open vSwitch on Ubuntu 16.04 URL: https://vpetersson.com/2018/01/28/openvswitch-and-kvm/ Date: 2018-01-28 If you're reading the [KVM/Networking documentation](https://help.ubuntu.com/community/KVM/Networking) for Ubuntu, you'll see that the recommended way to expose VMs to the world (public or private interface) is to use a Bridge. This was what I have been doing over the years. What you do realize however is that it becomes less than ideal when the network configuration is becoming complex. For instance, imagine that you're using two bonded interfaces (LACP) that you then expose to a bridge, which you in turn want to configure a set of VLANs on top of. That gets very messy using this method (and not to even mention the performance is poor in general). Enter [Open vSwitch](http://openvswitch.org/). Contrary to Bridge interfaces, Open vSwitch acts, as the name implies, as a proper virtual switch. This unfortunately means that the learning curve is a bit steeper, but once you get a hold of it, it's not too bad and you will see a significant performance increase. Also, kudos to SoulChild for a great write-up on the [pfSense forum](https://forum.pfsense.org/index.php?topic=139045.0). ## Server and infrastructure setup Before we begin, you will need the following: - A layer 2 switch (with VLAN support) - A modern server with Ubuntu 16.04 installed (the instructions will likely be similar on other OSes too) - Physical access to the server (with a keyboard) in case something goes wrong In this article, we're going to use the example of setting up a pfSense box as a VM with multiple VLANs exposed. We will assume the following VLAN setup and that you've already configured this in the switch: - VLAN 100 - WAN - VLAN 200 - LAN 1 (default) - VLAN 201 - LAN 2 - VLAN 202 - LAN 3 With the architecture planned out, let's get down and dirty and start by installing Open vSwitch on the server: ``` $ sudo apt-get install openvswitch-switch ``` Next, we need to configure Open vSwitch. First, we'll create a bridge called `ovsbridge` using the following command: ``` $ sudo ovs-vsctl add-br ovsbridge $ sudo ovs-vsctl set port ovsbridge tag=200 ``` With that done, you now need to modify your `/etc/network/interfaces` file. It most likely looked something similar to this now: ``` auto eno1 iface eno1 inet static address 192.168.200.4 netmask 255.255.255.0 gateway 192.168.200.1 dns-nameservers 8.8.4.4 8.8.8.8 ``` Now, in order for this to work with Open vSwitch, we need to make some changes to it and make it look like this: ``` auto eno1 iface eno1 inet manual auto ovsbridge iface ovsbridge inet static address 192.168.200.4 netmask 255.255.255.0 gateway 192.168.200.1 dns-nameservers 8.8.4.4 8.8.8.8 ``` Once done, we need to make a risky change. We now need to move the interface into the bridge and restart the server ``` $ sudo ovs-vsctl add-port ovsbridge eno1 \ tag=200 trunk=100,201,202 && \ sudo reboot now ``` If you're lucky, the server now comes back online and is accessible remotely directly. If not, you may need to delete the bridge and add it again slightly differently (depending on your switch): ``` $ sudo ovs-vsctl add-port ovsbridge eno1 \ tag=200 trunk=100,201,202 \ vlan_mode=native-tagged && \ sudo reboot now ``` ## VM configuration You can now create the pfSense VM using your preferred method. Mine is a combination `virt-manager` and `virsh`. Once you start the VM, edit the VM definition using `virsh edit`. Since we want to expose the VM to all our VLANs, we want to edit the network interface section to look something like this: ```
``` When you now boot up the VM, you should be able to access all of the specified VLANs in pfSense's setup and define them as outlined above. If however for some reason you only want to expose one VLAN to a given VM as an untagged VLAN, you can do so by using the following snippet: ``` ``` ## Final notes This should hopefully help you get started with Open vSwitch. It's a big topic, so there's plenty to learn and this article by no means intends to cover it all. I would also encourage you again to take a look at SoulChild's great write-up on the [pfSense forum](https://forum.pfsense.org/index.php?topic=139045.0) if you're having any issues. ## Update I have since posting this article moved to [Proxmox](https://www.proxmox.com/en/). While it doesn't support all OpenvSwitch features (such as trunking multiple VLANs on a single NIC to a VM), it is still worth it with the nice user interface and turn-key setup. --- # How to use jails on pfSense 2.4 URL: https://vpetersson.com/2018/01/27/jails-on-pfsense/ Date: 2018-01-27 **Disclosure:** _I have no idea how this impact the security of pfSense. There is probably a good reason why the jail service is disabled by default. Hence, beware that this might cause unexpected security issues as it is not a supported package._ Many, many years ago, I used to be an avid user FreeBSD jails. They were a great way to isolate services and boost security in the pre-Docker world. While I can't say I use FreeBSD much these days, I still run [pfSense](https://www.pfsense.org/) on more or less all my firewalls. It's easy to use, secure, and perhaps most importantly, very stable. Since pfSense is based on FreeBSD, its possible to run jails on it. I wouldn't encourage using this to run business critical applications, but I use it to run some minor non-external, non-essential services. In this tutorial, I will walk you through how to go from zero to jails on pfSense 2.4. We will also be using [ezjail](https://erdgeist.org/arts/software/ezjail) to simplify the management of the jails. ## Create a dedicated IP for the jail The first thing we need to do is to create a dedicated virtual IP for each jail. This is fairly straight forward in pfSense. Go to Firewall -> Virtual IPs and press 'Add.' ![Add a Virtual IP](/assets/pfsense_virtual_ip.webp) I would suggest that you only use the localhost interface, as I'm not sure what the security implications would be if you're exposing it to a WAN/LAN interface. I would also discourage from exposing any services in the jail. The reason for this is that if the service somehow gets compromised, the attacker would have free flow to all your network interfaces. It might be possible to mitigate this with firewall rules, but consider yourself warned. ## Prepare the system With the virtual IP(s) created, it's now time to prepare the server. Start by SSH'ing into the box and run the following commands. Install ezjail package: ``` $ pkg add http://pkg.freebsd.org/freebsd:11:x86:64/latest/All/ezjail-3.4.2.txz ``` Brute force link to make jails to work: ``` $ ln -s /lib/libkvm.so.7 /lib/libkvm.so.6 ``` Download missing jail file: ``` $ curl -o /etc/rc.d/jail https://raw.githubusercontent.com/freebsd/freebsd/stable/11/etc/rc.d/jail $ chmod +x /etc/rc.d/jail ``` Enable and initiate ezjail: ``` $ echo 'ezjail_enable="YES"' | tee -a /etc/rc.conf.local $ ezjail-admin install ``` ## Create a jail Next up, let's create a jail called 'labjail.local' with the IP 127.0.1.10: ``` $ ezjail-admin create labjail.local 'lo0|127.0.1.10' ``` We also need to fix a bug in the configuration for it to work: ``` $ sed -I \ -e 's/procfs_enable=\"YES\"/procfs_enable=\"NO\"/g' \ /usr/local/etc/ezjail/labjail_local ``` We can now start the jail by running: ``` $ ezjail start labjail.local ``` We can also verify the result by running: ``` $ ezjail list STA JID IP Hostname Root Directory --- ---- --------------- ------------------------------ ------------------------ DR 1 127.0.1.10 labjail.local /usr/jails/labjail.local ``` ## Accessing the jail Once the jail is up and running, you can access it by running: ``` $ ezjail console labjail.local ``` ## Summary You should now have your jail(s) up and running. They will however not auto-start when you reboot your pfSense server. You can read more about how to use and configure ezjail [here](https://erdgeist.org/arts/software/ezjail/). Happy hacking! --- # Using Kerberos.io with Docker and multiple cameras – Viktor Petersson – Medium URL: https://vpetersson.com/2017/06/04/using-kerberosio-with-docker-and-multiple-cameras/ Date: 2017-06-04 [Using Kerberos.io with Docker and multiple cameras – Viktor Petersson – Medium](https://medium.com/@vpetersson/using-kerberos-io-with-docker-and-multiple-cameras-9eee66dcae31) --- # Building a low powered NAS/backup target with a Raspberry Pi URL: https://vpetersson.com/2016/11/19/building-a-low-powered-nasbackup-target-with-a/ Date: 2016-11-19 [Building a low powered NAS/backup target with a Raspberry Pi](https://medium.com/@vpetersson/building-a-low-powered-nas-backup-target-with-a-raspberry-pi-98bb07802abb#.bffj5bnyu) --- # How to fix kernel_task CPU usage on macOS Sierra URL: https://vpetersson.com/2016/10/01/how-to-fix-kerneltask-cpu-usage-on-macos-sierra/ Date: 2016-10-01 In my post [How to fix kernel_task CPU usage on Yosemite](/2014/10/16/how-to-fix-kerneltask-cpu-usage-on-yosemite/), I first wrote about how a broken logic board can trigger high CPU usage from `kernel_task` as well as how to fix it. When El Capitan later were release, the issue [remained](/2016/01/03/how-to-fix-kerneltask-cpu-usage-on-el-capitan/). Now, with macOS Sierra out, I upgraded my old MacBook Pro (8,2) as well. As expected, the issue remained the same. Luckily, the same fix that I wrote about [El Capitan](/2016/01/03/how-to-fix-kerneltask-cpu-usage-on-el-capitan/) still worked. The only issue I had was that my system didn’t boot properly into recovery mode. Instead, I had to utilize a USB stick with the macOS Sierra installer on to be able to turn off System Integration Protection (SIP). If you run into the same problem, you can find the instructions for creating a bootable USB stick in my article [Create a bootable USB drive for Yosemite the easy way](/2014/09/18/create-a-bootable-usb-drive-for-yosemite-the-easy/). You will of course have to replace “Install OS X Yosemite Developer Preview.app” with “Install mac OS Sierra.app”. --- # HP LaserJet 500 colorMFP M570dn, Windows Server 2012 r2 and Offline mode URL: https://vpetersson.com/2016/08/16/hp-laserjet-500-colormfp-m570dn-windows-server/ Date: 2016-08-16 (I try hard to stay away from Windows environments, but sometimes it’s unavoidable.) Recently I ran across a strange issue with a HP LaserJet 500 colorMFP M570dn. The printer worked great for years, then all of the sudden, it started to act up. Applying the latest firmware didn’t solve the issue, so it started probing. The issue was that the printer randomly went into _Offline mode_, blocked all users from printing. After some probing, it turns out that the issue was related to [SNMP](https://social.technet.microsoft.com/Forums/office/en-US/80b06f0d-3b81-4cc6-95ad-aabc781d1eb0/printers-offline-in-windows-2008-r2?forum=winserverprint). In short, Windows tries to determine if the printer is offline using SNMP. This apparently is a [common issue](http://blog.rtwilson.com/how-to-fix-a-network-printer-suddenly-showing-as-offline-in-windows-vista/). I first struggled with some strange permission issues in Windows to toggle this SNMP setting, but I fortunately found [this guide](http://blacktheman.blogspot.se/2011/11/access-denied-message-when-changing.html) on how to work around the issue. --- # Provisioner URL: https://vpetersson.com/2016/08/02/provisioner/ Date: 2016-08-02 [Provisioner](https://provisioner.vpetersson.com/) I’ve been working on Provisioner for a while now and it’s starting to come together. Most recently I rolled out complete API documentation as well as a brand spanking new website (powered by Jekyll and Github Pages). I’m still trying to think of more use cases, so that’s part of what I will be working on next, as well as better documentation at large. I’ve also started to work on a Python library for easier integration work. Here are some use cases that I’m thinking of: - Bootstrap remote servers (original concept) - CI/CD assistance for pushing out playbooks to remote servers (store SSH Keys in Provisioner) - Perhaps integrating with Hashicorp Vault? - IFTT or similar integration (?) - PaaS support (Heroku/Deis etc) for easier deployment --- # Viktor Petersson, Screenly URL: https://vpetersson.com/2016/08/02/viktor-petersson-screenly/ Date: 2016-08-02 [Viktor Petersson, Screenly](http://sixteennine.podbean.com/e/viktor-petersson-screenly/) I was recently interviewed by Dave Haynes of Sixteen:Nine about Screenly. --- # How to migrate from qcow2/raw to iSCSI with KVM/QEMU URL: https://vpetersson.com/2016/07/29/how-to-migrate-from-qcow2raw-to-iscsi-with/ Date: 2016-07-29 I recently had to migrate a number of VMs currently running on an NFS share to an iSCSI target. During my research, I was surprised how little documentation there was around this, so I decided to whip up this quick little piece about how to do it. Here are the steps: - Create a new iSCSI target (one per VM/image you’re migrating). This step varies depending on your setup. - Attach target on server (I found [virt-manager](https://virt-manager.org/) suitable for the task). - Find the device from logs (/dev/sdc below). On Ubuntu, you will also need to install the package open-iscsi. - Run `qemu-img convert /path/to/your/image.qcow2 -O raw /dev/sdc` (for raw images, you could simply use `dd` but to keep things consistent, I opted for `qemu-img`). - Update the disk config in the VM definition to point to the iSCSI target. - Boot the VM and archive the old disk image. Voila! You should now have a VM running on iSCSI and you will likely receive better I/O performance. --- # Provisioner @ Ansible London URL: https://vpetersson.com/2016/05/19/provisioner-ansible-london/ Date: 2016-05-19 Here’s my deck from tonight’s talk at Ansible London.

--- # Digital signage solution, Screenly, chooses Canonical’s Ubuntu Core URL: https://vpetersson.com/2016/05/18/digital-signage-solution-screenly-chooses/ Date: 2016-05-18 [Digital signage solution, Screenly, chooses Canonical’s Ubuntu Core](https://insights.ubuntu.com/2016/05/18/digital-signage-solution-screenly-chooses-canonicals-ubuntu-core/) Super excited to announce that Screenly partners with Ubuntu/Canonical to bring Screenly to Ubuntu Core. --- # How Weave Net Enables a Global Docker Cluster with OnApp URL: https://vpetersson.com/2016/05/16/how-weave-net-enables-a-global-docker-cluster-with/ Date: 2016-05-16 [How Weave Net Enables a Global Docker Cluster with OnApp](https://www.weave.works/weave-global-docker-cluster/) I guest blogged a bit on Weave’s blog today. --- # Deck from IoT London URL: https://vpetersson.com/2016/04/21/deck-from-iot-london/ Date: 2016-04-21 Here’s my deck from my recent IoT London talk: --- # How to fix kernel_task CPU usage on El Capitan URL: https://vpetersson.com/2016/01/03/how-to-fix-kerneltask-cpu-usage-on-el-capitan/ Date: 2016-01-03 Sometime ago, I wrote the blog post [How to fix kernel_task CPU usage on Yosemite](/2014/10/16/how-to-fix-kerneltask-cpu-usage-on-yosemite/). This post still receives a great amount of traction, so I wanted to post an update that reflects the covers how to do this on El Capitan. The process is largely the same, but requires a bit more work due to the changes to the additional security that El Capitan introduced to the file system with System Integration Protection (SIP). The tl;dr is as follows: - Boot up the system in Recovery Mode (Cmd+R on boot). Start a Terminal window and run `csrutil disable`. - Reboot the system as normal and follow the same steps as in the original guide. - Reboot the system again into Recover Mode and enable SIP by running `csrutil enable`. - Reboot the system. # Step 1: Disable System Integration Protection (SIP) First, shut down your computer. Then power the computer on and boot it into [Recovery Mode](https://support.apple.com/en-gb/HT201314) by holding down Command + R. Once the computer is done booting, bring up a Terminal window (Utility -> Terminal). With that done, simply run the following command: $ csrutil disable That will disable SIP. In order for this to work, you now need to reboot your computer into regular mode (i.e. not anoter Recover Mode boot). # Step 2: Fix the issue Once your computer is booted, stara Terminal session and run the following commands (for more information, see the [original post](/2014/10/16/how-to-fix-kerneltask-cpu-usage-on-yosemite/): # Find the model $ system_profiler -detailLevel mini | grep "Model Identifier:" Model Identifier: MacBookPro8,2 # Move and backup the file $ cd /System/Library/Extensions/IOPlatformPluginFamily.kext/Contents/PlugIns/ACPI_SMC_PlatformPlugin.kext/Contents/Resources $ sudo mv MacBookPro8_2.plist MacBookPro8_2.bak With that done, it’s time to go back into Recover Mode again, so shut down your computer. # Step 3: Re-enable SIP Boot the computer in Recover Mode again by pressing Command + R on boot. Again, open a Terminal window, but this time, run the following command: $ csrutil enable Now reboot your computer and you should be all set. **Update:** If you’re having issues with mac OS Sierra, please [How to fix kernel_task CPU usage on macOS Sierra](/2016/10/01/how-to-fix-kerneltask-cpu-usage-on-macos-sierra/) --- # Using Ansible with Google Cloud Platform (the easy way) URL: https://vpetersson.com/2015/12/04/using-ansible-with-google-cloud-platform-the-easy/ Date: 2015-12-04 For some time, [Ansible](http://www.ansible.com/) has been my configuration management of choice and we use it for both [Screenly](http://www.screenlyapp.com) and [YippieMove](http://www.yippiemove.com). Since both of these services are running on Google Compute Engine, we’re using Ansible’s [dynamic inventory](http://docs.ansible.com/ansible/intro_dynamic_inventory.html) for GCE. (Behind the scenes, this dynamic inventory is using [Apache Libcloud](https://libcloud.apache.org/), which is a great Python library for interacting with various providers.) When I first followed Ansible’s [Google Cloud Platform Guide](http://docs.ansible.com/ansible/guide_gce.html) I did run into a fair bit of trouble with authentication. As it turns out, Libcloud is very picky when it comes to the environment variables that you need to set. To solve this, I whipped together a little script that I call on to set the appropriate variable environments for each project I’m working on. This saved me a lot of headache. The script looks as follows: #!/bin/bash export GCE_PROJECT=your-project export GCE_PEM_FILE_PATH=~/.gce/$GCE_PROJECT\.json export GCE_EMAIL=$(grep client_email $GCE_PEM_FILE_PATH | sed -e 's/ "client_email": "//g' -e 's/",//g') gcloud config set project $GCE_PROJECT Just change `GCE_PROJECT` to match your setup, and then run: $ source /path/to/script.sh You can now run Ansible with the GCE inventory file. As an added bonus, this also configures `gcloud` to the same project. Happy (DevOps) hacking. --- # How to find a network device when using Internet Sharing on Mac OS X URL: https://vpetersson.com/2015/11/19/how-to-find-a-network-device-when-using-internet/ Date: 2015-11-19 The built-in Internet Sharing in OS X is very handy. When I’m on the road, I frequently use this to share my laptops WiFi connection with other devices over a wired connection (such as a Raspberry Pi, when I work on [Screenly](http://www.screenlyapp.com)). ![Internet Sharing on OS X](/tumblr_files/tumblr_inline_ny2ky0ZeW01skxjxc_540.webp) If you’re connecting some kind of headless device, you will likely want to connect to this device over SSH or similar. The only problem is that you don’t know the IP address of said device (it’s headless, remember). Luckly, all the tools you need are already available on OS X. All you need to do is to fire up the Terminal: ![arp scan and SSH](/tumblr_files/tumblr_inline_ny2kysKTry1skxjxc_540.webp) Simple as pie. --- # Quickly navigate folders in your shell with `ccd` URL: https://vpetersson.com/2015/10/24/quickly-navigate-folders-in-your-shell-with-ccd/ Date: 2015-10-24 I’m a huge fan of [autojump](https://github.com/wting/autojump). It allows me to quickly navigate my filesystem in ways without having to type out every folder. There is however one task that I frequently that I wanted to make more efficient: create a new folder and then jump into said folder. Normally, this would simply be: $ mkdir foo $ cd foo This feels somewhat inefficient, so I wrote a little tool to help with this called `ccd`: $ ccd foo The tool is very simple, but saves me a number of keystrokes every day. There are two pieces to the tool: one bash script and one entry in `~/.profile`. ### ~/bin/ccd.sh #!/bin/bash ARG="$1" if [ ! -d "$ARG" ]; then echo "Creating $ARG." mkdir -p "$(pwd)/$ARG" else echo "$ARG already exists." fi cd "$(pwd)/$1" Once you have installed the script, just set the right permission with `chmod +x ~/bin/ccd.sh` ### ~/.profile Lastly, you will need to add the following entry to your `~/.profile` file: alias ccd="source ~/bin/ccd.sh" Finally, either reload your shell, or run `source ~/.profile`. --- # Introducing NatPass URL: https://vpetersson.com/2015/10/01/introducing-natpass/ Date: 2015-10-01 After moving to London, I decided to go with [NatWest](http://www.natwest.com) as my bank. While it was a good experience at large, their online banking leavs a lot to be desired. Not only do they lack things like Two-Factor Authenciation (2FA), but they also have this _really_ frustrating login system. Well, today I had enough and whipped up a CLI tool that allows you to generate the data that they ask for by reading it in from 1Password (using [1pass](https://pypi.python.org/pypi/1pass/0.2.1)). The result is a tool called [NatPass](https://github.com/vpetersson/natpass). It will save me lots of frustration, and hopefully it will save someone else the same frustration. --- # A case study in failed UX/UI  (aka DSC please get your shit together) URL: https://vpetersson.com/2015/09/15/a-case-study-in-failed-uxui-aka-dsc-please-get/ Date: 2015-09-15 Dear Digital Security Controls (DSC), I recently purchased your [T-link](http://www.dsc.com/index.php?n=products&o=view&id=143) extension (IP module) for two alarm systems that I wanted to be able to remotely manage. After a few hours of troubleshooting (read: having to install Windows XP), I am now able to connect to these alarm systems over a VPN connection, but that’s about it. Please take a look at this screenshot: ![DSC DLS alarm software](/tumblr_files/tumblr_inline_nuqfgtj0GQ1skxjxc_540.webp) If it isn’t obvious what is wrong with this, let me spell this out for you. - Your software only runs on Windows XP. Nope, not even Windows 7. Forget about a modern web interface like most people have come to expect in this day and age from an ‘IP module’. - Your client software requires SQL Server Express (ehh WTF?!). - Throughout the entire app, you use the industry standard icon that means 'refresh’ for 'Restore all options as to original’. I just almost hit this icon when I was about to reload the values. - In a client software _download_ means just that, down load (i.e. fetching to your local computer) and _upload_ means to upload from the computer to whatever device/service you’re intereacting with. In your software, you’ve apparently decided to call them the other way around (which is very confusing). - The user interface is confusing to say the least. I’ve worked with a few different alarm systems in the past. They’ve all been ranging from crappy to less crappy, but this one takes the price. After spending a good 15 minutes, I still have _no_ idea how to perform even basic tasks (like user management, set PIN codes, look at events etc). I could go on and on about this, but really, I don’t have enough energy. Anyone with _any_ UI/UX experience can show you. I’m just severely disappointed that some has the stomach to sell something this poorly designed in this day and age. It should barely be classified as a Minimum Viable Product (MVP). If you want to be in the software space, please hire people who are actually qualified for the task. Yours truly,\ A one-time customer --- # “War dialing” with Skype URL: https://vpetersson.com/2015/09/08/war-dialing-with-skype/ Date: 2015-09-08 (Fine, this isn’t [war dialing](https://en.wikipedia.org/wiki/War_dialing) in the 90s context, but it made for a good title.) As furious I am with Lufhansa’s [strike](http://www.independent.co.uk/news/business/lufthansa-passengers-grounded-as-pilots-launch-two-day-strike-10491256.html), it did give me time to write a fun script. With the announcement of the strike, Lufthansa’s customer service lines were brought to their knees. After trying to manually call a few of them (in different countries), I finally gave up with the manual mode and instead decided to automate this. The result looks like this: ![](/tumblr_files/tumblr_inline_nuddo9DjCQ1skxjxc_540.webp) Using the [Skype4Py Python module](https://pypi.python.org/pypi/Skype4Py), it was able to automate the redail process with realtive ease. While I wasn’t able to get it working on Mac OS X, I was able to get it running in an Ubuntu VM more or less out-of-the-box. The final script looks like this: --- # How to set up syslog-ng with TLS on Logentries URL: https://vpetersson.com/2015/08/06/how-to-set-up-syslog-ng-with-tls-on-logentries/ Date: 2015-08-06 After using Loggly for a few years, I stumbled across [Logentries](https://logentries.com/learnmore?code=82a5c804) (disclosure: referral link). What I was looking for was basically something that is more affordable when the volume increases, and Logentries is a lot more affordable than Loggly as you grow. If you are just starting out using a remote shipping target (be Loggly, Papertrail or Logentries), it is worth noting that if you simply follow the instructions **you will be shipping logs in plain text**. Since you’re a smart cookie, you know that’s a very bad idea. The good news is that most services (at least the three mentioned) do support encryption, but it takes a bit more work. What I did find however was that the instructions for Logentries in particular were pretty bad, so I decided to share this with you to save you the effort it took me to get this working with syslog-ng. ## Step 1: Fetch the certificates This was surprisingly challenging with Logentries. While they openly announce that they support encryption, the links to **where you can find said certificates** wasn’t as obvious. Long story short, you can find them [here](https://logentries.com/doc/certificates/). Unfortunately, they do simply give you a URL, but instead encourage you to copy and paste the certs by hand (which is of course far more error prone). The certificates that you need are the ones under ‘API certificate’. Go ahead and save the first certificate as `api.crt` and the intermediate certificate as `intermediate.crt`. In theory, you should now verify the hashes, but I wasn’t able to get mine to match, so perhaps they’ve simply forgot to update the hashes. If it’s to any help, this is what I got: $ md5 *.crt MD5 (api.crt) = 9107ba5545a000ea06cd5fd046102c14 MD5 (intermediate.crt) = 413a2acb5b07cd49cbf916eefcf3ba33 (Please note that this of course will change whenver Logentries updates their certificates.) With these certificates, we now need to generate a combined certificate that we’ll ship to the nodes. To do this, simply run: $ cat {intermediate.crt,api.crt} > logentries_full.crt If you’re lazy, you can also fetch mine from [here](https://gist.githubusercontent.com/vpetersson/e9965d8e27aa0a2a71c7/raw/c911ec6bb11c4866ff7c8cdc01052d8998887bf2/gistfile1.txt). The md5sum for this certificate should be: $ md5 logentries_full.crt MD5 (logentries_full.crt) = 3918fcd927bb98fa2c23a46e5a4b7820 (Again, this will of course also change whenver Logentries updates their certificates.) Now create the directory `/etc/syslog-ng/keys/ca.d/` on the host and copy `logentries_full.crt` certificate there. ## Step 2: Configure syslog-ng With the certificate in place, the last step is to create the config file. My preference is to use the `conf.d` folder for this (which already exists on Debian/Ubuntu installations). Now create the file `/etc/syslog-ng/conf.d/22-logentries.conf` and populate it with the following data: template logentriesTemplate { template("YOURTOKEN $ISODATE $HOST $PROGRAM $MSG\n"); template_escape(no); }; source s_all { unix-stream("/dev/log"); }; destination d_network_logentries { tcp("api.logentries.com" port(20000) tls(peer-verify(required-untrusted) ca_dir("/etc/syslog-ng/keys/ca.d/")) template(logentriesTemplate) ); }; log { source(s_all); destination(d_network_logentries); }; Replace YOURTOKEN with the actual token from Logentries. You also might change some elements in this file to better fit your needs, but what’s important here is the destination-block, which is what is dealing with the TLS aspect. With the file in place, restart syslog-ng and you should be good to go. ## Troubleshooting If you’re having issues getting this started, the first thing to do is to check the actual logs. There should be a line similar to this if it worked: Syslog connection established; fd='11', server='AF_INET(a.b.c.d:20000)', local='AF_INET(0.0.0.0:0)' You might also want to sniff the traffic to verify that the traffic sent out actually is encrypted. You can do that easily with `tcpdump` as follows: $ tcpdump -A dst api.logentries.com Happy hacking! --- # An update on YippieMove URL: https://vpetersson.com/2015/07/17/an-update-on-yippiemove/ Date: 2015-07-17 [An update on YippieMove](http://wireload.net/news/2015/07/an-update-on-yippiemove.html) --- # Deck from Open Cloud Day (2015) URL: https://vpetersson.com/2015/06/17/deck-from-open-cloud-day-2015/ Date: 2015-06-17 Yesterday I had the pleaseure to speak at [Open Cloud Day](http://www.ch-open.ch/events/aktuelle-events/160615-open-cloud-day-2015/) in Bern, Switzerland. The linup for the event was good and I’m happy to contribute to the thriving open source and tech community in Switzerland. It was also great to hang out with the fine folks over at [ICCLAB](http://www.s-i.ch/) and [ZHAW](http://www.unibe.ch/). **Update:** The recording of the talk is available [here](https://www.youtube.com/watch?v=pHdc3f98Kxs&index=11&list=PLofS3lNZckseu0v_CP4XjgDUQxRKfF6gA) --- # Modern document management for your startup URL: https://vpetersson.com/2015/05/18/modern-document-management-for-your-startup/ Date: 2015-05-18 [Modern document management for your startup](http://wireload.net/news/2015/05/modern_document_management.html) We’re currently in the process of revamping how we deal with documents at [WireLoad](http://wireload.net). Here are some thoughts on this and how we do things today. --- # Manage Docker resources with Cgroups URL: https://vpetersson.com/2015/05/12/manage-docker-resources-with-cgroups/ Date: 2015-05-12 [Manage Docker resources with Cgroups](https://www.cloudsigma.com/manage-docker-resources-with-cgroups/) My latest blog-post about Docker and Cgroups is live at CloudSigma’s blog. --- # Metrics on the big screen URL: https://vpetersson.com/2015/04/21/metrics-on-the-big-screen/ Date: 2015-04-21 [hosted-graphite](http://blog.hostedgraphite.com/post/117001786117/metrics-on-the-big-screen): > One of the best ways to ensure your whole team knows what your technology is doing is to ensure that everyone has quick and easy access to your dashboards. One of the best ways to do that is to get your dashboards displayed on a giant screen right in the middle of your work area.  > > Sometimes this can be a bit of a pain - you need to devote some machine to power the dashboard and pipe it to the TV as well as the hassle of setting it up. Enter Screenly! > > ![image](/tumblr_files/tumblr_inline_nmr13oDHr61rwljgd_540.webp) > > Screenly is a simple Raspberry Pi-based platform that hooks directly into a HDTV and turns it into an easily configurable digital sign! Hosted Graphite is pleased to announce we’re partnering with Screenly to ensure that ops teams can get their [Grafana dashboards](https://www.hostedgraphite.com/hosted-grafana/) displayed easily and quickly. They’ve made it ridiculously easy to get your dashboards in front of everyone, and provide a handy [guide](http://www.screenlyapp.com/use-cases/dashboard/hostedgraphite.html) to get started. > > Haven’t tried Screenly yet? [Check it out here!](https://login.screenlyapp.com/signup) --- # An introduction to cgroups and cgroupspy URL: https://vpetersson.com/2015/04/18/an-introduction-to-cgroups-and-cgroupspy/ Date: 2015-04-18 Here’s my presentation from [ApacheCon](http://apachecon.com/) in Austin, TX.

I’m happy that I was invited to speak at the conference as it was full of awesome and talented people.

--- # Receive calls with Google Voice (over VoIP) for free URL: https://vpetersson.com/2015/04/07/receive-calls-with-google-voice-over-voip-for/ Date: 2015-04-07 I’ve been using Google Voice for a long time. In fact, I started using it back when it was called Grand Central (which was an acquisition). It’s a great product, and I really love it. Unfortunately it has more or less been left untouched for the last few years. If you’re in the U.S., Google Voice works great. You can just forward your Google Voice to your cell phone and you will automatically receive calls there. Much of the time however, I’m not in the U.S.. As digital nomad, I usually on the road and constantly swap SIM cards (which accidentally is why I created [NomadSIMs.io](http://nomadsims.io)). Because I constantly switch SIM card, it becomes even more important to have a persistent number that can be re-routed to a number that I can be reached at. My first approach to this problem was to simply purchase a Skype-In number and have my Google Voice number forwarded to this number when I’m outside of the U.S.. Unfortunately this doesn’t work very well, as Skype will try to take over the voicemail (along with a number of other issues). Today I had to revisit this problem as I had to receive a to my Google Voice number to verify a purchase. My initial idea was was to do something with Twilio. However, since you currently cannot connect a SIP softphone to Twilio, that turned out to be a no-go. After some more research, I was ran across [CallCentric](http://www.callcentric.com). While the site looks like a relic from the 90s, they do offer something relevant: **a free US number with free inbound calls** ([direct link](http://www.callcentric.com/coverage/free_phone_number)). This is exactly what I needed. When you’ve signed up, you’ll get a New York number that you can add to your Google Voice. Next, you need to get a softphone/VoIP client for your desktop. I first tried out [Telephone](https://itunes.apple.com/us/app/telephone/id406825478?mt=12), but that didn’t work well. I then tried [X-Lite](http://www.counterpath.com/x-lite-download/), which isn’t very pretty, but it works. With all that set up, you can now receive inbound calls on your Google Voice number wherever you are in the world. For outbound calls I still use Skype (with their Unlimited plan). --- # Using cgroups with Docker on Ubuntu 14.04 URL: https://vpetersson.com/2015/04/05/using-cgroups-with-docker-on-ubuntu-1404/ Date: 2015-04-05 As I was working on my upcoming presentation at [ApacheCon](http://www.apachecon.com/), I was playing a little bit with cgroups inside Docker. What I found was that there aren’t a whole lot of documentation on this, so I figured I put together a quick blog post about it. ## Enable the LXC driver Assuming you already have Docker installed on Ubuntu 14.04, you will still need to enable the LXC driver. To do this, you will need to do the following $ sudo apt-get install -y lxc $ echo 'DOCKER_OPTS="--exec-driver=lxc"' \ | sudo tee -a /etc/default/docker $ sudo service docker restart ## Spin up two containers without cgroup policy Let’s start by launching two containers that each will max out the CPU (by running `md5sum /dev/urandom`). $ docker run -d busybox md5sum /dev/urandom $ docker run -d busybox md5sum /dev/urandom ![Docker containers running without cgroup policy](/tumblr_files/tumblr_inline_nmbyklbgPw1skxjxc_540.webp) As expected, we can see that these containers fully utilize one core each. ## Spin up two containers with cgroup policy Now let’s put the new LXC options to use by adding two cgroup policies. What we’re looking to do is to the same workload as before, but run them on the same core. We would then expect them to occupy 50% of the core each. However, we want to give one container 75% of the CPU share, and the other only 25%. To accomplish this, we use ‘cpu.shares’ to divvy up the CPU and 'cpuset.cpus’ to lock the containers to the same core. Start container with low priority: $ docker run -d --name='low_prio' \ --lxc-conf="lxc.cgroup.cpu.shares=250" \ --lxc-conf="lxc.cgroup.cpuset.cpus=0" \ busybox md5sum /dev/urandom Start container with high priority: $ docker run -d --name='high_prio' \ --lxc-conf="lxc.cgroup.cpu.shares=750" \ --lxc-conf="lxc.cgroup.cpuset.cpus=0" \ busybox md5sum /dev/urandom ![Docker containers running with cgroup policy](/tumblr_files/tumblr_inline_nmbyk9EJyj1skxjxc_540.webp) As you can see, it worked! Happy hacking! **Update:** For more details about how to use Docker with Cgroups, please take a look at my blog post [Manage Docker resources with Cgroups](https://www.cloudsigma.com/manage-docker-resources-with-cgroups/) over at CloudSigma’s blog. --- # How to parse and dump a sitemap URL: https://vpetersson.com/2015/03/31/how-to-parse-and-dump-a-sitemap/ Date: 2015-03-31 When deling with website migrations, you sometimes need to map out the old content such that you can create your redirect to the new pages. While doing this, I ran across this little helpful snippet.

Just modify the URL to the sitemap and the script will print out all the pages. You will need BeautifulSoup 4 and Requests.

--- # On the Secure Messaging Community URL: https://vpetersson.com/2015/03/30/on-the-secure-messaging-community/ Date: 2015-03-30 [On the Secure Messaging Community](http://blog.nadim.computer/post/114970275623/on-the-secure-messaging-community) [nadimkobeissi](http://blog.nadim.computer/post/114970275623/on-the-secure-messaging-community): > This afternoon, WIRED published an [article](http://www.wired.co.uk/magazine/archive/2015/03/features/messaging-apps) praising [Telegram](http://www.wired.co.uk/magazine/archive/2015/03/features/messaging-apps), a mobile messaging app, for its privacy features and “hardcore encryption”. Telegram is an open source messenger that provides some encryption features — but it has come under legitimate scrutiny due to the exotic and unstudied… --- # Growth Hacking for Lean Startups URL: https://vpetersson.com/2015/03/23/growth-hacking-for-lean-startups/ Date: 2015-03-23 Just ran across this great slide deck today over at GrowthHackers. --- # Are they using Google Apps? URL: https://vpetersson.com/2015/03/18/are-they-using-google-apps/ Date: 2015-03-18 [Are they using Google Apps?](http://aretheyusinggoogleapps.com/) Google Hangouts is great tool for video conferences…if the other party is also on the Google stack (Gmail or Google Apps). If they’re not, it it’s a dead end. Often time when scheduling conference calls, I ended up manually looking at the MX records to determine if I should use Google Hangout or [Uber Conference](https://www.uberconference.com/). Since this process got old quickly, I hacked together a quick webapp that can do the lookup. --- # Pebble as a pedometer URL: https://vpetersson.com/2015/03/16/pebble-as-a-pedometer/ Date: 2015-03-16 For some time, I’ve worn both my Pebble and a Fitbit Flex. Since the Pebble comes with a built-in pedometer, this bothered me a lot. As a digital nomad, you’re always looking at ways to reduce the things you carry around and I’m now happy to report that I have retired my Fitbit. ![](/tumblr_files/tumblr_inline_nlb8qzinZ91skxjxc.webp) There has long been various pedometer watchfaces available for the Pebble. However, none of them (to my knowledge) could actually do anything with the data. At least to me, it is then rather pointless. Enter the [Jawbone Up Watchface](https://apps.getpebble.com/applications/5429c7b7dfdebeb8dd000021) for Pebble. Simply install the app from the Pebble App Store and you’re ready to go. The watchface will connect to the Up service and report your steps as well as displaying the data in a nice fashion.  Simple as pie. You can also download the [Jawbone Up app](https://itunes.apple.com/us/app/up-by-jawbone-free-fitness/id916240764?mt=8) for iOS to view the data. This also allows you to push your data to Health. --- # » CoreOS is now available on CloudSigma! URL: https://vpetersson.com/2015/03/16/coreos-is-now-available-on-cloudsigma/ Date: 2015-03-16 [» CoreOS is now available on CloudSigma!](https://www.cloudsigma.com/2015/03/10/coreos-is-now-available-on-cloudsigma/) I’m really excited to announce that CoreOS is now available on CloudSigma. --- # GetCanary.io - Intelligent notifications URL: https://vpetersson.com/2015/03/14/getcanaryio-intelligent-notifications/ Date: 2015-03-14 [GetCanary.io - Intelligent notifications](http://getcanary.io/) The other day I had a Eureka moment while archiving the daily batch of notifications from various server tasks. Instead of having to read over ‘success’ messages, why isn’t there a reliable way to instead get notified when things do not work as expected. This is what our latest project sets out to solve. The title of the project is GetCanary.io, and the waitlist is open now. --- # My deck from CloudExpo Europe URL: https://vpetersson.com/2015/03/13/my-deck-from-cloudexpo-europe/ Date: 2015-03-13 Here’s the presentation deck from CloudExpo Europe 2015. The title of the talk was “Server Evolution: From mainframes to containers and PaaS.” Enjoy! --- # Thoughts on (practical) privacy URL: https://vpetersson.com/2015/02/27/thoughts-on-practical-privacy/ Date: 2015-02-27 Privacy is difficult in this day and age. On the one hand we want to limit what we provide these data hungry companies with and on the other hand, we don’t want to alienate ourselves from our friends. Personally, Facebook is the company that I’m most reluctant to share data with. I simply don’t trust them. If you read Salim Virani’s post [Get your loved ones off Facebook](http://saintsal.com/facebook/), you will understand why. The same argument could be made about Google and Apple of course, but both of them appears to take privacy at least a tad more serious than Facebook. The problem is that most of your friends will likely be on Facebook (and communicate using Facebook Messaging and WhatsApp). Assuming you don’t want to cut the cord with Facebook entirely, what can you do to limit the data you share with them? Here are some measures I’ve taken to improve my privacy. # Desktop If you’re concerned about sharing data with Google, then use Firefox. Regardless of your browser, you should _really_ get these two plugins: - [Ghostery](https://www.ghostery.com/en/): Regain control over your browser and block beacons etc. - EFF’s [HTTPS Everywhere](https://www.eff.org/https-everywhere): Only HTTPS wherever possibly. For Facebook, I’ve moved to using the [Tor Browser](https://www.torproject.org/projects/torbrowser.html.en) exclusively. I then use Facebook’s [Onion address](https://www.facebookcorewwwi.onion) to access the service. I realize the irony of using Facebook over Tor, but it will at least prevent GeoIP lookups and sandbox Facebook to one browser. # Mobile Make sure to remove all Facebook applications (both Messenger and the main app). These are major violators of your privacy. You may also want to remove WhatsApp. If you need to access Facebook on the go, use the web version. You may also want to disable the location service when you don’t need it. # Instant Messaging For most people, instant messaging is part of your everyday routine. There are however good and bad ones. EFF have put together a [Secure Messaging Scorecard](https://www.eff.org/secure-messaging-scorecard) that ranks various messaging apps. **Update:** Richard Stallman just published a post titled [Reasons not to use Facebook](https://stallman.org/facebook.html) which outlines a large number of reasons why you should avoid Facebook. --- # ZoneMinder as a people counter? URL: https://vpetersson.com/2015/02/01/zoneminder-as-a-people-counter/ Date: 2015-02-01 In recent years, it has become fairly common within the retail space to use (IP-based) surveillance cameras to track foot traffic (this can also be referred to as a “people counter”). This is a great idea, as it allows you to re-use existing hardware instead of having to install additional sensors. There are numerous commercial solutions out there that are capable of doing this. I have however not been able to find any complete open source product that can track foot traffic (at least not bi-directional). I did find a few examples of OpenCV based projects, such as [this one](https://www.youtube.com/watch?v=OWab2_ete7s), but no source code. That got me thinking; [ZoneMinder](http://www.zoneminder.com/) is a popular open source video surveillance tool. Since I spent some time developing a Virtual Machine, I'm somewhat familiar with it. Not only does it support a large number of cameras (both V4L devices and IP cameras), it also comes with a number of trigger and alert abilities. Using ZoneMinder, we should in theory be able to create a people counter. In a perfect world, the camera would be located in a hallway and pointed directly down from the ceiling, but it should work otherwise too. Here’s what you would do: - Configure your camera(s) in ZoneMinder. - Define two zones narrow zones such that all people will pass through both zones (let’s call these “z1” and “z2”). For instance, if a person comes into the store, they’d first pass through z1, and then z2. If they were to leave the store, the customer would pass z2 and then z1. While I haven’t tested the next step, in theory, it would be possible to set up an alarm/filter based on this. Since these filters can execute external commands, it would be simple to make ZoneMinder call a script, which in turn triggers a ping to some database, such as [Grafana](http://grafana.org/), [Graphite](http://graphite.wikidot.com/) or similar. Since I haven’t tested this last step, I don’t know if this is possible. However, if it does work, this would allow us to create a very cost effective people tracker. I should also point out that my initial idea was to use a Raspberry Pi and attach some sort of IR sensor (over USB), but that would require additional hardware. --- # Thought on the Purism laptop URL: https://vpetersson.com/2015/01/24/thought-on-the-purism-laptop/ Date: 2015-01-24 Today I stumbled across the [Purism](http://puri.sm/)’s Librem 15 laptop. It’s a [crowd sourced](https://www.crowdsupply.com/purism/librem-laptop) laptop that is, as the name implies, pure. No proprietary or firmware or software. I _really_ like this idea, as it improves security by a lot. ![](/tumblr_files/tumblr_inline_nioe4yPZK61skxjxc.webp) At first glance, it looks pretty good. However after diving into the details, it falls short on a few points. - The keyboard layout. A number pad? Is this a laptop designed for accountants? Those are probably the only people still using the number pad. Also, because of this the ‘center’ of the keyboard is moved to the left, which makes it look unbalanced. - A CD drive? How often do you _really_ use a CD drive? I use a few time a year tops. For those few occasions, it’s easier to use an external drive. The extra weight does not justify this. - The form factor. 13" is the new 15". People carry their laptops with them everywhere these days. Weight and size matters a lot. Most people I know are either using 11" or 13" laptops. Don’t get me wrong, I _really_ want these guys to succeed. The task of creating a laptop from scratch is far from trivial, so I salute them. Unfortunately the points above are show-stoppers for me. If these guys can release a 13" version with a sane keyboard and no CD drive, I’m definitely getting one. --- # A "dead man's switch" for your computer? URL: https://vpetersson.com/2015/01/22/a-dead-mans-switch-for-your-computer/ Date: 2015-01-22 In the last few weeks, a lot of details have been disclosed around [Ross Ulbricht’s arrest](http://arstechnica.com/tech-policy/2015/01/silk-road-trial-fbi-reveals-whats-on-ross-ulbrichts-computer-in-open-court/). For those not familiar with the matter, Ulbricht was arrested at a library in San Francisco some time ago with his laptop open. The agents managed to steal the laptop out of Ulbricht’s hands and therefore prevent him from locking the computer (which presumably had full-disk encryption). This got me thinking; why don’t we have **Dead Man’s Switches** for computers? It would be very simple to create one. Having such device would not only be useful if you’re a high profile target (like Ulbricht), but also to conveniently lock your computer in an office environment. # Using a USB stick ## Hardware Using just things we have laying around, we should be able to design the most primitive version. All you really need is a USB stick and some strings (or lanyard) - Format your USB drive with a some random file on. - Make a bracelet out of strings or a lanyard. The string from the bracelet must be long enough such that it isn’t in the way while you’re typing/working. Imagine a modified version of [this](https://www.etsy.com/listing/84615477/scandinavian-usb-stick-leather-braided). ## Software With the hardware ready, we now need a daemon or similar that runs on your computer and checks for this file. If this file disappears (i.e. you remove the USB device), the computer will lock down. Here’s an example of a very primitive version of such script. All it does is to check for the file every second, and if it is absent, it will lock the computer (assuming you’re using OS X): #!/bin/bash # Where is the file located? # Should be something like "/Volumes// on OS X. WATCHFILE="/path/to/file" while true; do if [ -f "$WATCHFILE" ]; then sleep 1 else /System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend fi done _(This script is clearly more meant as a proof-of-concept than for usage in a high security environment.)_ # Using wearables There are a lot of issues with the approach above. First and foremost, it is likely not be very comfortable to work with a strap around your wrist all day. You’re also likely to accidentally drag your laptop off of your desk in an unexpected move. Secondly, having such device around your wrist is going to look very suspicious (and odd). So what can we do about this? Given the raise of wearables, there are now a ton of different options available at our disposal. For instance, we could use a [Fitbit](http://www.fitbit.com/) or [Pebble](https://getpebble.com/) for the same purpose. However, instead of checking for a file, we would check for the proximity of the device over Bluetooth. If the the computer loses connection to the device, it auto-locks. There are however problems with Bluetooth, as the range can get a little too good for this particular situation. As such, we’d likely have take into account the signal strength. Perhaps we lock the computer if the signal strength goes below a certain level. Using some sort of wearable technology would clearly require more work than simple USB drive version above, but it would look a lot less suspicious and also also be a lot more convenient. **Update:** As pointed out by MrMid and Lennaert van Dijke, there is a tool for Linux that can lock your computer with Bluetooth proximity claled [BlueProximity](http://blueproximity.sourceforge.net/). I’d love to see that ported to OS X. **Update 2:** It appears as there is a tool called [Proximity](https://code.google.com/p/reduxcomputing-proximity/) that claims to be able to do this, but I couldn’t get it to work on Yosemite. **Update 3:** After some additional research, I’ve found a number of OS X apps that can do this, including [TokenLock](https://itunes.apple.com/us/app/tokenlock/id402433482), [Keycard](https://itunes.apple.com/us/app/keycard/id578513438) and [HandyLock](http://www.netputing.com/applications/handylock/). Unfortunately all of these apps implement their own lock screen. This is presumably because they want to have the ability to unlock the system too using the same mechanism. The problem with this is that you significantly lower the security. The search goes on for an app that can _only_ lock down the system using the built-in tools. I’m not interested in unlocking it. **Update 4:** I finally found it! The app is called [Bluetooth Screen Lock](https://itunes.apple.com/us/app/bluetooth-screen-lock/id509251123). Unfortunately it doesn’t work with my Fitbit Flex, but it _does_ work just great with my Pebble. ![](/tumblr_files/tumblr_inline_niuu6f5eIe1skxjxc.webp) The one thing I did notice however (which is specific to the Pebble), is that you need to also pair the device with the computer. If not, you can only use it while the watch is Bluetooth Discovery mode (i.e. in the menu). **Update 5:** While the locking with the Pebble does work great, there is an issue with it – you cannot have the Pebble paired with your phone simultaneously. As such, you unfortunately need to decide if you want to get notifications from your phone _or_ use it to lock your computer. --- # Interview with Viktor Petersson, VP of Business Development for IAAS provider CloudSigma URL: https://vpetersson.com/2015/01/20/interview-with-viktor-petersson-vp-of-business/ Date: 2015-01-20 [Interview with Viktor Petersson, VP of Business Development for IAAS provider CloudSigma](http://blog.syncsort.com/2015/01/interview-viktor-petersson-vp-business-development-iaas-provider-cloudsigma/) > Interview with Viktor Petersson, VP of Business Development for IAAS provider CloudSigma on what it takes to be a Gartner-Cool Vendor --- # Virtualization on Ubuntu 14.04 with virsh and vmbuilder URL: https://vpetersson.com/2015/01/18/virtualization-on-ubuntu-1404-with-virsh-and/ Date: 2015-01-18 While I’ve switched most of my workloads to [Docker](https://www.docker.com), there are still some situations where you need to manage and set up Virtual Machines (VM). These days, KVM+QEMU has more or less been established as the virtualization standard, so we’ll be using that. Setting this up on Ubuntu 14.04 (Trusty Tahr) is very straight forward. All you need to do is to run: $ sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils Assuming everything went well, you should now be up and running and can list your VMs (there are of course none): $ sudo virsh list Id Name State ---------------------------------------------------- If something went wrong, take a look at the [KVM installation guide](https://help.ubuntu.com/community/KVM/Installation). If you only need Usermode Networking (i.e. no public IP), you’re done now. If you however need your VMs to be accessible from the outside, you need to set up [bridged networking](https://help.ubuntu.com/community/KVM/Networking#Bridged_Networking). With your system up and running, let’s create a VM using `vmbuilder`: $ sudo vmbuilder kvm ubuntu \ --suite trusty \ --flavour virtual \ --addpkg=linux-image-generic \ --addpkg=unattended-upgrades \ --addpkg openssh-server \ --addpkg=acpid \ --arch amd64 \ --libvirt qemu:///system \ --user ubuntu \ --name ubuntu \ --hostname=test \ --pass default _Due to [this](http://serverfault.com/questions/590114/vanilla-ubuntu-vm-builder-on-i7-aborts-aborts-with-pae/591369#591369) bug, you will need to use the “linux-image-generic” kernel instead of regular virtual one._ Creating the VM will take some time. Once it has been created you can start the VM using `virsh` with: $ virsh start test In order to `ssh` into the server, we need to find the server IP. There are a few ways to find this, but here’s the best technique I’ve found: $ sudo virsh dumpxml test | grep 'mac address' $ arp -an | grep 'XX:YY:ZZ:XX:YY:ZZ' ? (192.168.122.135) at XX:YY:ZZ:XX:YY:ZZ [ether] on virbr0 Next, `ssh` into the VM: $ ssh ubuntu@ _The password is defined above as ‘default’_ As you probably noticed, those are a fair amount of variables we need to pass onto `virsh`. The easiest solution to get around this is to create a config file. Porting the variables above into a config file would leave us with something like this: [DEFAULT] arch = amd64 user = ubuntu name = ubuntu pass = default tmpfs = - [ubuntu] suite = trusty flavour = virtual addpkg = openssh-server, unattended-upgrades, acpid, linux-image-generic [kvm] libvirt = qemu:///system With this config file, we can create a VM by simply running: $ sudo vmbuilder kvm ubuntu -c myfile.cfg A more complete config file, along with important hooks for re-generating SSH host keys on first boot can be found [here](https://help.ubuntu.com/community/JeOSVMBuilder#Using_configuration_files). There’s a lot more to `virsh` and `vmbuilder`, but this should help you get started. ## Caveats - In `virsh` VMs are referred to as 'domains.’ This might be a bit confusing at first. - To stop and delete a VM in `virsh`, run `destroy test` and then `undefine test --managed-save`. - You probably want to use the `--mem`, `--cpus` and `--rootsize` options when using `vmbuilder` (see the [man page](http://manpages.ubuntu.com/manpages/trusty/en/man1/vmbuilder.1.html)). - Using the `--hostname` is handy when creating new VMs can be very handy. --- # Sync Dev: Securely Deploying Sync URL: https://vpetersson.com/2014/12/19/sync-dev-securely-deploying-sync/ Date: 2014-12-19 [Sync Dev: Securely Deploying Sync](http://blog.bittorrent.com/2014/12/18/sync-dev-securely-deploying-sync/) BitTorrent’s blog re-posted by Sync article today. --- # » How to securely use BitTorrent Sync for backups URL: https://vpetersson.com/2014/12/10/how-to-securely-use-bittorrent-sync-for-backups/ Date: 2014-12-10 [» How to securely use BitTorrent Sync for backups](https://www.cloudsigma.com/2014/12/10/how-to-securely-use-bittorrent-sync-for-backups/) I just published an article on CloudSigma.com on how to securely deploy BitTorrent Sync in the cloud with step-by-step instructions. --- # How to deal with/archive an old WordPress site URL: https://vpetersson.com/2014/12/04/how-to-deal-witharchive-an-old-wordpress-site/ Date: 2014-12-04 We all have those old blogs that we started some time ago with some grandiose vision. Unfortunately, the blog never really took off. Now it just sits there and generate a small amount of traffic every day. It’s enough to not shut it down, but not enough to invest a whole lot more resources into. Chances are that said blog is running on WordPress. You now have to log into the blog somewhat frequently to upgrade it. Moreover, chances are also that your blog has been compromised during this time and you don’t even know about it. Sounds familiar? I’ve had a few of these, and today I finally had enough and decided to do something about it. Here are the steps I took to migrate two of our old WordPress sites to static files. (Also take a look at my other post [How to migrate from WordPress to Tumblr](/2014/07/24/how-to-migrate-from-wordpress-to-tumblr/)) ## Backup the files and database Before we begin, you should make sure that you have backups of both the files and database. ## Add the site to Google Webmaster Tools If you haven’t already, make sure to add your site to [Google Webmaster Tools](https://www.google.com/webmasters/). This will allow us to monitor potential errors that could cause SEO penalties. It is also worth adding the site to [Bing Webmaster](https://www.bing.com/webmaster) to ensure Bing don’t find anything either. ## Bring the site up to date First, we want to make sure that it is up to date. While it might not matter too much (since we are moving it to a static page anyways) I felt better knowing that I had brought it up to date. Also, given how easy this is to do in WordPress, there is little reason not to do this (other than perhaps incompatible themes etc). ## Scan it using WordFence It is likely that your WordPress installation has been compromised. As such, I strongly recommend that you scan it using [WordFence](https://wordpress.org/plugins/wordfence/) first. For both the sites that I migrated, I found issues. Resolve these issues before moving on. ## Generate a Sitemap If you have a sitemap, make sure that it is up to date (i.e. refresh it). If you don’t, make sure you generate one with a tool like [Google Sitemap Generator](https://wordpress.org/plugins/google-sitemap-generator/). ## Creating a static copy My first approach was to use the WordPress plugin [Very Static](https://wordpress.org/plugins/really-static/) to generate a full static copy of my installation. While this plugin does a great job at generating a static copy, it is designed to run in conjunction with your WordPress installation. As such, I wasn’t able to get it to run independently (it refused to use ’/’ as the root, which caused all links in the static files to be wrong). Since Very Static failed, I resorted to the good old tool `wget` to fetch a copy for me. On the same server, I simply created a new folder and ran: $ mkdir static.my-wordpress-blog.com $ cd static.my-wordpress-blog.com $ wget --mirror -nH -k -E http://www.my-wordpress-blog.com $ wget http://www.my-wordpress-blog.com/sitemap.xml This will take some time. When done, you should have a complete static copy of your WordPress installation. I also looked at the output and noticed that one of the sites had a lot of spam there that I manually removed. This was probably as a result of some SQL Injection attack that WordFence was unable to detect. ## Change document root After verifying that `wget` was able to retrieve all files, the last step was to update the document root. Depending on your web server, this will differ. On Nginx, all I had to do was to change the ‘root’ stanza from ’/path/to/www.my-wordpress-blog.com’ to ’/path/to/static.my-wordpress-blog.com’. I also disabled all PHP specific configuration options just to speed things up further. --- # Screenly is featured in the latest issue of Linux User URL: https://vpetersson.com/2014/11/20/screenly-is-featured-in-the-latest-issue-of-linux/ Date: 2014-11-20 ![Linux User](/tumblr_files/tumblr_inline_nfces4YkL41skxjxc.webp) I’m really excited to let you know that Screenly is featured in the latest issue of Linux User in the U.K. (issue 146). A snippet of the article is already up on their [website](http://www.linuxuser.co.uk/news/win-a-hdmipi-screenly). We’ve also teamed up with Linux User to give a way a free 12 month subscription of [Screenly](http://www.screenlyapp.com), along with a [HDMIPi](http://hdmipi.com/). --- # The dangers of UFW + Docker URL: https://vpetersson.com/2014/11/03/the-dangers-of-ufw-docker/ Date: 2014-11-03 In recent years, I’ve transitioned over to using Ubuntu’s [UFW](https://help.ubuntu.com/community/UFW). In most cases, it gets the job done and it is easy to manage via provisioning tools like [Ansible](http://www.ansible.com). As turns out however, using UFW together with Docker can be _very_ dangerous as I will show below. Let’s start with an Ubuntu 14.04 server. It has UFW and Docker installed already, so let’s start by configuring UFW to block everything but SSH. $ ufw allow ssh [...] $ ufw default deny incoming [...] $ ufw enable [...] $ sudo ufw status Status: active To Action From -- ------ ---- 22 ALLOW Anywhere 22 (v6) ALLOW Anywhere (v6) That looks good. The default policy is set to deny all incoming traffic, and we only poke hole on port 22. Let’s move on to Docker. For this example, we’ll use the latest version as of this writing. $ docker version Client version: 1.3.1 Client API version: 1.15 Go version (client): go1.3.3 Git commit (client): 4e9bbfa OS/Arch (client): linux/amd64 Server version: 1.3.1 Server API version: 1.15 Go version (server): go1.3.3 Git commit (server): 4e9bbfa Let’s now spin up a MongoDB server to listen on 0.0.0.0:27017. While a bad security practice, the firewall **should** block all external connections to it. $ docker run -d -p 27017:27017 --name mongodb dockerfile/mongodb [...] $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f07c734038c5 dockerfile/mongodb:latest "mongod" 5 seconds ago Up 4 seconds 28017/tcp, 0.0.0.0:27017->27017/tcp mongodb With the server up and running, we can see that it is listening as expected. I will now try to connect to my MongoDB server from my laptop on the public interface: $ mongo --host a.b.c.d MongoDB shell version: 2.6.5 connecting to: a.b.c.d:27017/test Welcome to the MongoDB shell. For interactive help, type "help". For more comprehensive documentation, see http://docs.mongodb.org/ Questions? Try the support group http://groups.google.com/group/mongodb-user Wait, what?! That shouldn’t be possible! Let’s take another look at UFW. $ ufw status Status: active To Action From -- ------ ---- 22 ALLOW Anywhere 22 (v6) ALLOW Anywhere (v6) That still looks fine, so how did this happen? As it turns out, Docker tampers directly with `iptables`. $ iptables -L | grep 27017 ACCEPT tcp -- anywhere 172.17.0.2 tcp dpt:27017 It is expected that Docker tampers with the firewall rules to some extent. It is after all what enable Docker containers to bind on a port. Yet, this behavior is not what I would have expected. So what’s the moral of the story here? - UFW doesn’t tell you `iptables` _true_ state (not shocking, but still). - **Never** use the `-p` option (or `-P`) in Docker for something you don’t want to be public. - **Only** bind on either the loopback interface or an internal IP. ## Update [@karl_grz](https://twitter.com/@karl_grz) correctly pointed out that it is possible to override this behavior by adding `--iptables=false` to to the Docker daemon. This is also described [here](http://docs.docker.com/articles/networking/#communication-between-containers). It still beats me why this isn’t the default configuration. On Ubuntu, you can edit `/etc/default/docker` and uncomment the DOCKER_OPTS line: DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4 --iptables=false" After doing so, you need to restart Docker with `service restart docker`. I also tested this and can confirm that I was able to connect to MongoDB on the host, but **not** from my laptop on the public interface. Thanks for pointing that out, Karl. --- # How to get BitTorrent Sync to stop syncing Finder meta data URL: https://vpetersson.com/2014/10/31/how-to-get-bittorrent-sync-to-stop-syncing-finder/ Date: 2014-10-31 I really like [BitTorrent Sync](https://www.btsync.com/en/). It’s a great alternative to Dropbox et al, without having to hand over your unencrypted data to a third party. Unfortunately, the default configuration in BitTorrent Sync for Mac OS X is somewhat odd. As it turns out, it syncs all of Finder’s meta data. As you can imagine, this means a lot of pointless syncing. After [posting](http://forum.bittorrent.com/topic/32376-ignorelist-isnt-honored-by-the-sending-node/) on the forum, I did however [found out](http://sync-help.bittorrent.com/customer/portal/articles/1682051-alt-streams-and-xattrs-in-sync) that by default these files are added to a file called `StreamsList` that apparently overrides the more obvious `IgnoreList`. Why these files are listed in there beats me, but after removing them from there, my nodes stopped syncing like crazy all the time. --- # » An introduction to server provisioning with CloudInit URL: https://vpetersson.com/2014/10/29/an-introduction-to-server-provisioning-with/ Date: 2014-10-29 [» An introduction to server provisioning with CloudInit](https://www.cloudsigma.com/2014/10/29/an-introduction-to-server-provisioning-with-cloudinit/) I just published an article over at CloudSigma’s blog about server provisioning using CloudInit. --- # Running Puppet master in Docker URL: https://vpetersson.com/2014/10/26/running-puppet-master-in-docker/ Date: 2014-10-26 In the past, I’ve relied a lot on Puppet for automation. That means that I have a fair number of Puppet masters at my disposals. Puppet is a Ruby based application. As many Ruby based applications, it depends on a fairly large number of other libraries. Having to install all these packages on a brand new server is a pity. In addition, the recommended approach is to run the Puppet master with Apache and [passenger](https://www.phusionpassenger.com/), which complicates things further if are running another web server, such as Nginx, on the same server. I like to keep my servers tidy and clean, hence having to install all these dependencies bothers me. This is where Docker comes it play; by putting things inside a container, you can avoid polluting the host system and also make the setup less complex. This weekend I decided to try to create a _dockerized_ Puppet master. There were a fair number of quirks that I had to sort out, but I eventually got it to work well, and I’m happy with the result. To create your own Puppet master using my container, all you need to do is to run the following command: docker run -d \ --name puppetmaster \ --restart always \ -h puppet.local \ -p 8140:8140 \ -e 'ACLGRANT=a.b.c.d/24' \ -v /path/to/datastore:/var/lib/puppet \ -v /path/to/modules:/etc/puppet/modules \ -v /path/to/manifests:/etc/puppet/manifests \ -t vpetersson/puppetmaster The command above might seem a bit overwhelming, and you can find more detailed information about the command and how to use the container over at its home at [github](https://github.com/vpetersson/docker-puppetmaster). In the next few days, I’m planning to roll out this in production and I’m looking forward to simplifying my future Puppet deployments. --- # yay blotter is currently the 9th best selling URL: https://vpetersson.com/2014/10/22/yay-blotter-is-currently-the-9th-best-selling/ Date: 2014-10-22 ![](/tumblr_files/tumblr_ndupg9oYsS1thwdtao1_1280.webp) Yay, [Blotter](http://wireload.net/products/blotter/) is currently the 9th best selling productivity app in the U.S. Mac App Store! --- # Keyboard hacks for OS X URL: https://vpetersson.com/2014/10/22/keyboard-hacks-for-os-x/ Date: 2014-10-22 Every time I install OS X, I get very frustrated with the default keyboard settings. Perhaps the most frustrating one is how slow the navigation is when you’re using the arrow keys to move around in text blocks. Fortunately, it is easy to change the settings. To make the keyboard navigation faster, you need to toggle the following two settings: ![Change keyboard speed on Mac OS X](/tumblr_files/tumblr_inline_ndumzj5FgR1skxjxc.webp) The next thing I always do too is to re-map the Caps Lock key. I really cannot think of a single time I’ve used it in the last five years other than by mistake. Yet it sits on prime keyboard real estate. Luckily, this is also easy to change. Just pop open the ‘Modifier Keys’ dialogue and change Caps Lock to whatever you find most useful; for me this is Control (since it is used heavily in most unix/linux applications, such as tmux). ![Remap Caps Lock to Ctrl on Mac OS X](/tumblr_files/tumblr_inline_ndun0oiROh1skxjxc.webp) There you have it. After changing these settings, you should be able to move around quickly on Mac OS X. --- # Run your Tor Relay Node in Docker URL: https://vpetersson.com/2014/10/16/run-your-tor-relay-node-in-docker/ Date: 2014-10-16 [Tor](https://www.torproject.org) is a project that I really love to support. In this age of increased surveillance, Tor is needed more than ever. Unfortunately, your own Tor Exit Node can put you in a lot of trouble due to the fact that Tor being used for all kinds of abuses. This is unfortunately a the reality of anonymity; some people will abuse it. Yet, we shall not let this overshadow the fact that Tor is a critical tool for or **a lot** of people out there who are trying to circumvent government censorship. I want to help **these people** with the resources I have available at my disposal. As a result, I have run a number of Tor servers over the years. If you don’t want to take the risk of running your own Exit Node, you can still help by running a Relay Node. To simplify the deployment of this (and to increase the security of your server), I’ve created a [Docker container](https://github.com/vpetersson/docker-torrelay) that does just that. With Docker installed, all you need to do is to run the following: $ sudo docker run -d \ -p :9001:9001 \ --restart=always \ --name=torrelay \ -t vpetersson/torrelay This will create a Tor Relay Node. All you need to do is to open up your firewall, such that the Docker container is reachable on port 9001. Update: I just updated the name of the container added more info. Please check the github page for more details. --- # How to fix kernel_task CPU usage on Yosemite URL: https://vpetersson.com/2014/10/16/how-to-fix-kerneltask-cpu-usage-on-yosemite/ Date: 2014-10-16 Yesterday I had to hand in my almost new MacBook Pro (Retina) for repair due to a broken logic board. This meant that I had to go back to my old laptop for a little bit. In preparation for this, I dusted off my old MacBook Pro this weekend so that I would have something from in the meantime. Since it was due for an upgrade, I decided to [install the Yosemite beta](/2014/10/05/how-to-install-yosemite-and-ubuntu-linux/) on it just to give it a try. After getting everything up and running, I did however notice that the laptop was rather unresponsive and sluggish. After further analysis, I noticed that it was something odd with the `kernel_task` process hogging up resources, pegging the CPU at maximum utilization. ![kernel_task hogging resources](/tumblr_files/tumblr_inline_ndj4vrolbe1skxjxc.webp) Spending a few minutes on thy mighty Google, I found multiple other people who were having similar issues across Mac models and OS X versions. It appears that in case there is something wrong with the Mac, there is a chance that `kernel_task` will completely trash your system. For me, it was likely caused by a broken sound card. Since I was getting pretty desperate, I decided to try [Rhys Oxenham’s](http://www.rdoxenham.com/?p=259) suggestion, and simply disable the kernel extension for the particular model. As crazy as this may sound, it did indeed do the trick. My laptop is now **a lot** more responsive, and `kernel_task` is no longer consuming my entire CPU. Rhys’ page includes a lot more details, but in summary, here’s what I did: # Find the model $ system_profiler -detailLevel mini | grep "Model Identifier:" Model Identifier: MacBookPro8,2 # Move and backup the file $ mkdir -p ~/backup $ cd /System/Library/Extensions/IOPlatformPluginFamily.kext/Contents/PlugIns/ACPI_SMC_PlatformPlugin.kext/Contents/Resources $ sudo mv MacBookPro8_2.plist ~/backup/ After moving the file above and rebooting the system, things looked a lot more sane. ![More sane kernel_task](/tumblr_files/tumblr_inline_ndj4y6oLJl1skxjxc.webp) Please note that **this is risky business** and there might be side-effects. It did do the trick for me, but **I don’t take any responsibility for damaged hardware**. It’s all on you. **Update:** After about a week, I can confirm that this had no negative impact at all as far as I can tell. Battery life and performance was improved. **Update 2:** Since El Capitan was released, a lot of people have been having issues applying this fix. Since it is a bit different, I’ve published a new post that covers how to do this on El Capitan: [How to fix kernel_task CPU usage on El Capitan](/2016/01/03/how-to-fix-kerneltask-cpu-usage-on-el-capitan/). --- # CloudSigma joins Ubuntu Certified Public Cloud URL: https://vpetersson.com/2014/10/15/cloudsigma-joins-ubuntu-certified-public-cloud/ Date: 2014-10-15 [CloudSigma joins Ubuntu Certified Public Cloud](https://insights.ubuntu.com/2014/10/15/cloudsigma-joins-ubuntu-certified-public-cloud/) Happy to announce a partnership deal that I’ve been working with for some time. --- # Sync Dev: Using Sync For Backups In The Cloud (With Docker) URL: https://vpetersson.com/2014/10/14/sync-dev-using-sync-for-backups-in-the-cloud/ Date: 2014-10-14 [Sync Dev: Using Sync For Backups In The Cloud (With Docker)](http://blog.bittorrent.com/2014/07/17/sync-dev-using-sync-for-backups-in-the-cloud-with-docker/) Here’s a guest blog post that I wrote on Bittorrent’s blog a while back that I forgot to cross-post here. --- # Sophisticated new playlist editor in Screenly URL: https://vpetersson.com/2014/10/10/sophisticated-new-playlist-editor-in-screenly/ Date: 2014-10-10 [Sophisticated new playlist editor in Screenly](http://wireload.net/news/2014/10/sophisticated_new_playlist_editor_in_screenly.html) I’m very excited about this latest release we just did to Screenly. --- # How to install Yosemite and Ubuntu Linux side-by-side with full disk encryption URL: https://vpetersson.com/2014/10/05/how-to-install-yosemite-and-ubuntu-linux/ Date: 2014-10-05 This weekend I spent a bit of time playing around with my old MacBook Pro. My goal was to set it up as a backup/test laptop. What I wanted to accomplish was the following: - Install Yosemite with Full Disk Encryption (FDE) - Install Ubuntu Linux 14.04 LTS with FDE (using LVM) As it turns out, this was a lot more challenging than I thought. My initial approach was to simply split the disk into two partitions and just install each operating system separately. Once installed, I was planning to simply enabled FileVault inside OS X (Ubuntu allows you to enable encryption during the installation). Unfortunately, it wasn’t that easy. Here are some observation: - If you install Yosemite on the full disk and enable FDE, you cannot resize the disk (neither using `hdiutil` or Disk Utility). - If you install Yosemite on one of the two partitions, you cannot enable FileVault inside OS X. This is probably due to the lack of Recovery partition[*](https://discussions.apple.com/thread/3222378?start=0&tstart=0). The solution to my problem was to fire up Disk Utility inside the Yosemite installer, split the disk into two partition. With that done, I then re-formatted the OS X partition as ‘Journaled, Encrypted’. This allowed me to install Yosemite just fine. Afterwards I could install Ubuntu (which was never a problem) onto the other partition. To switch between the OS’s, I simply hold down the option key on boot. The only drawback with this approach as far as I can tell is that you lose the OS X recovery partition. For me however, that’s not a big deal, as I can always boot it off of a USB drive or SD card. A guide on how to create such device can be found [here](/2014/09/18/create-a-bootable-usb-drive-for-yosemite-the-easy/). --- # Autojump: blazing fast filesystem navigation URL: https://vpetersson.com/2014/10/03/autojump-blazing-fast-filesystem-navigation/ Date: 2014-10-03 [Autojump: blazing fast filesystem navigation](https://github.com/joelthelion/autojump) > autojump - A cd command that learns - easily navigate directories from the command line I just ran across a very convenient little tool called `autojump` that I wanted to share. For a number of years, i’ve used aliases in Bash for quickly navigating the file system. `autojump` makes this unnecessary. # OS X crash course This requires that you have `brew` installed and use Bash as your shell ## Installation brew install autojump echo '[[ -s `brew --prefix`/etc/autojump.sh ]] && . `brew --prefix`/etc/autojump.sh' >> ~/.bash_profile source ~/.bash_profile ## Usage cd /path/to/my-project j my-project Please note that you need to `cd` into the directory before it appears in the index. --- # Create a bootable USB drive for Yosemite the easy way URL: https://vpetersson.com/2014/09/18/create-a-bootable-usb-drive-for-yosemite-the-easy/ Date: 2014-09-18 Today I decided to take Yosemite for a spin on my old laptop. Since installing from USB is the only way to do a _clean_ install, I started googling around for exact steps (which were somewhat messy). To my surprise, it appears as Apple also realized that this was messy and decided to bake in a solution for this. It isn’t however completely obvious, but here is the command: $ sudo /Applications/Install\ OS\ X\ Yosemite\ Developer\ Preview.app/Contents/Resources/createinstallmedia --volume /Volumes/your_flash_drive / --applicationpath /Applications/Install\ OS\ X\ Yosemite\ Developer\ Preview.app/ ![](/tumblr_files/tumblr_inline_nc4cdfJwWd1skxjxc.webp) **Protip:** If you want to speed things up, use a Class 10 SD card instead of an USB drive. These cards are faster than most vanilla USB drives, and any modern Mac will boot off of them just fine. For more information on how to use this, please see [this](http://support.apple.com/kb/HT5856) page. --- # How to not screw up localization on websites URL: https://vpetersson.com/2014/09/04/how-to-not-screw-up-localization-on-websites/ Date: 2014-09-04 Poorly implemented translations ‘logic’ is something that really grinds my gear. Today I ran across the new [Dyson 360 Eye](http://www.dyson360eye.com/) and it is a case study in how to **not** do website translations. Since I’m connected to a VPN in Switzerland, this is what I was presented with: ![](/tumblr_files/tumblr_inline_nbdymqMeeW1skxjxc.webp) As you can see, the website is in German just because I connected through Switzerland. Now let’s analyze why this is bad. # Location != language Your location isn’t really a great way to determine the language for a user. For instance, there are plenty of companies that route all their internal traffic through a central end-point for all their remote offices. Using this approach, all them will see the language of the country the end-point is located. There are of course a ton of other situations that would break this logic too, such as VPN users (like yours truly) and incorrect GeoIP lookups. A far better approach is to look at the locale of the browser, and user that as the basis. That way, you can serve the user with the language s/he prefers (regardless of location). # Use proper URLs Another thing you can notice in the Dyson example is that URL doesn’t change with the locale. For instance, one would expect something like `dyson360eye.com/de` or `dyson360eye.de` for the German version. Instead, you just have the German version being served under `dyson360eye.com`. Beyond being a horrible thing from an SEO perspective, it is also confusing for the users. # Give the users a choice Even if you have a good implementation for auto-probing for the users’ language (either using GeoIP or locale), you should still give the users a choice to override this. In the case of Dyson, there is no way for me to change the language, **nor** override it manually by altering the URL. # What should I do? It’s easy to criticize, so let’s give Dyson some constructive feedback instead: - Don’t use the same URL for all languages. Use proper suffix (such as `/de` or a separate subdomain). - Use proper redirects for each language to not upset thy mighty Google Bot. - Give the user a choice to set the language themselves (regardless how good you think your logic is). --- # Linux Performance Tools URL: https://vpetersson.com/2014/08/21/linux-performance-tools/ Date: 2014-08-21 [Linux Performance Tools](http://www.slideshare.net/brendangregg/linux-performance-tools) A great collection of Linux tools for troubleshooting and improving performance. --- # Using chain certificates with Nginx URL: https://vpetersson.com/2014/08/20/using-chain-certificates-with-nginx/ Date: 2014-08-20 I’m a big fan of [Namecheap](http://namecheap.com). They offer cheap SSL certificates that does the trick just fine. For most my projects, I go for their cheapest option (usually ‘PossitiveSSL’). Every time I deploy one of these certificates however, I screw up the order of the chain certificates. The tl;dr is that certificate should go first, and then the supporting chain certificates. After extracting the zip-file you get from Namecheap, simply run this command to create your certificate: $ cat STAR_*.crt AddTrust*.crt COMODORS*.crt > foobar.com.crt --- # DEFCON 22 Badge Challenge URL: https://vpetersson.com/2014/08/19/defcon-22-badge-challenge/ Date: 2014-08-19 This is _very_ cool. The guys at Defcon sure knows how to geek out and create a challenge. Here’s the [Badge Challenge Walkthrough](http://potatohatsecurity.tumblr.com/post/94565729529/defcon-22-badge-challenge-walkthrough). --- # TripIt insecurely broadcasts sensitive travel details in calendar feeds; could destroy your vacation URL: https://vpetersson.com/2014/08/18/tripit-insecurely-broadcasts-sensitive-travel/ Date: 2014-08-18 [TripIt insecurely broadcasts sensitive travel details in calendar feeds; could destroy your vacation](http://httpshaming.tumblr.com/post/94950343491/tripit-insecurely-broadcasts-sensitive-travel-details) [httpshaming](http://httpshaming.tumblr.com/post/94950343491/tripit-insecurely-broadcasts-sensitive-travel-details): > ![image](/tumblr_files/tumblr_inline_naf9uuajul1smov0f.webp) > > Although I love using TripIt to organize my travel, if you subscribe to a TripIt calendar feed using an application like OS X or iOS Calendar, **details about your past and upcoming travel is sent plaintext, unencrypted out over the net:** > > - Your name > - Trip summaries (where you’re going and… --- # Introducing nomadsims.io URL: https://vpetersson.com/2014/08/08/introducing-nomadsimsio/ Date: 2014-08-08 A while back, I stumbled across [NomadList](http://nomadlist.io/). It’s basically a crowd sourced list of cities around the world ranked by how good they are for remote working. That got me thinking. One of the pain points I’ve had over the years when traveling is finding the best local mobile carrier for pre-paid cards. Wouldn’t it be great if there was a crowd sourced database for this? So I spent some time yesterday hacking together a rough prototype for this that I call [NomadSIMs](http://nomadsims.io/). The page source is available on Github, and the database is simple JSON. Anyone can help contribute to the list by just issuing a pull request. I’ve already populated the database with some data I had readily available, but really need help from other travelers/digital nomads out there. Let’s build out this list and make it awesome! --- # What's in your bag, Viktor? URL: https://vpetersson.com/2014/08/03/whats-in-your-bag-viktor/ Date: 2014-08-03 [What's in your bag, Viktor?](http://leannomads.tumblr.com/post/93698602869/whats-in-your-bag-viktor) [leannomads](http://leannomads.tumblr.com/post/93698602869/whats-in-your-bag-viktor): > One of the most important possessions as a digital nomad is your laptop bag and its contents. Think of it as your office. > > Since you will be carrying this bag with you almost everywhere you go, you really want to make sure that it is robust, and yet light (when filled). After years of testing out… --- # how funny i got a cloudflare timeouton URL: https://vpetersson.com/2014/07/30/how-funny-i-got-a-cloudflare-timeouton/ Date: 2014-07-30 ![](/tumblr_files/tumblr_n9ison2ogl1thwdtao1_1280.webp) How funny. I got a CloudFlare timeout…on [www.cloudflare.com](http://www.cloudflare.com) --- # How to boot from USB with Grub2 URL: https://vpetersson.com/2014/07/29/how-to-boot-from-usb-with-grub2/ Date: 2014-07-29 Yesterday, I had to rescue a broken Ubuntu 14.04 installation by booting from USB. Unfortunately, I was unable to get into the BIOS to change the boot order (because of a BIOS password and a bad memory). Fortunately, since I was able to get to Grub, it was still possible. Here’s how I did it: - Create a bootable USB drive (using something like [Startup Disk Creator](https://apps.ubuntu.com/cat/applications/usb-creator-gtk/). Before taking the drive out, locate where the `vmlinuz` and `initrd.*` files are located. You’ll need them later. - Insert the USB drive and boot the system. When you get to Grub, press _c_ to get to the ‘command-line’ option. Here is where it gets a bit tricky. In my case, I knew the root partition on the USB disk was `/dev/sda1`, yours may vary. Since Grub uses a slightly different device mapper, let’s use it to find the partitions: grub> ls (hd0) (hd0,msdos5) (hd1) (hd1,msdos0) This will show you the available devices. In my case, the relevant partition was `(hd1,msdos1)`. Now, let’s use this information, along with our knowledge of where the `vmlinuz` and `initrd` files are to boot the system: grub> linux (hd1,msdos1)/install/vmlinuz root=/dev/sdb1 grub> initrd (hd1,msdos1)/install/initrd.gz grub> boot That’s it. You should now be able to boot straight into Ubuntu. This should even work if your BIOS doesn’t support booting off of USB. --- # hold on let me just upgrade and reboot my light URL: https://vpetersson.com/2014/07/27/hold-on-let-me-just-upgrade-and-reboot-my-light/ Date: 2014-07-27 ![](/tumblr_files/tumblr_n9dwt7mk481thwdtao1_1280.webp) Hold on, let me just upgrade and reboot my light bulbs… --- # DevOps tools/trends that I really like right now... URL: https://vpetersson.com/2014/07/26/devops-toolstrends-that-i-really-like-right/ Date: 2014-07-26 - [**Docker**](https://www.docker.com/) and **Docker Hub** \- It’s awesome and it removes a lot of the complexity from traditional DevOps work. What’s funny is that while doing my migration to Tumblr, I found this [three year blog post](/2010/08/28/shouldnt-dependencies-on-core-components-be-isolated/) that pretty much describes Docker. Yes, it’s not too far from FreeBSD’s Jails, but it never had this momentum nor the toolset (which of course is a result of the momentum). - [**CoreOS**](https://coreos.com/), **Etcd**, **Systemd** and **Fleet** \- Once you have started to fully embrace Docker, CoreOS and its components just makes a ton of sense. I really hope that this is where we’re heading. Once you’ve had a taste of `fleetctl`, you’re hooked. - [**Ansible**](http://www.ansible.com) \- Sometimes you still need to manage servers and Ansible is a breeze of fresh air. Sorry, Puppet, but I’m done with you. - [**Python**](https://www.python.org/) \- Yes, in modern day DevOps, Bash simply won’t cut it. Python is a blessing when you need to interact with an API or similar. --- # How to migrate from WordPress to Tumblr URL: https://vpetersson.com/2014/07/24/how-to-migrate-from-wordpress-to-tumblr/ Date: 2014-07-24 Today, I’ve spent a large chunk of my day migrating my blog from WordPress to Tumblr and [About.me](http://about.me/vpetersson). Before you ask why on earth I’d do this, let me just sum it up in a few bullet points: - I hate PHP and managing WordPress sites. Yes, it has gotten a lot better lately, but it’s still a big pain in the ass. - Most of the content I generate is either on other blogs, or fed via Twitter. As a result, I don’t blog too much. Now, you’re likely reading this post because you’re interested in the subject at hand, so let’s dive into it. (You may also find my article [How to deal with/archive an old WordPress site](/2014/12/04/how-to-deal-witharchive-an-old-wordpress-site/) interesting) Yet, before we start, there are a few pre-requisites for this to work: - You’re moving from one subdomain (or domain) to another. In my case, I’m moving from ‘viktorpetersson.com’ to 'blog.viktorpetersson.com’ - Your old blog will still be online (the redirect and images will still be served from here) - You use Nginx as your web server (but this can probably be changed pretty easily by adjusting the rewrite rules in the script). - You already have Tumblr up and running with your subdomain/domain. - You need a Tumblr API key. You can sign up for this [here](https://api.tumblr.com/console). The backbone of my migration was two WordPress plugins, so let’s get started by installing them: - [Google XML Sitemaps](http://www.arnebrachhold.de/redir/sitemap-home/) - [Tumblr Crosspost](https://github.com/meitar/tumblr-crosspostr/#readme) Start by installing these plug-ins into your WordPress site. Make sure to also activate them. # Migrating the content to Tumblr Thanks to the plugin **Tumblr Crosspostr**, migrating the content over to Tumblr is a breeze. First, you need to configure the plug-in by adding your Tumblr API keys. Before we begin the migration/import, make sure you don’t have the setting in Tumblr that will automatically post to Twitter or Facebook. If you do, you’ll spam your own wall with all these posts. With that done, go to **Tools** -\> **Tumblrize Archive** -\> **Tumblrize Everything!**. # Getting a list of source pages Once you’ve installed the sitemap-plugin, you should be able to go to [http://your-site.com/sitemap.xml](http://your-site.com/sitemap.xml) and copy and paste the content of into a text-file on your computer. It should look something like: https://vpetersson.com/ 100% Daily 2013-08-02 13:21 https://vpetersson.com/2013/08/02/are-they-using-google-apps/ 20% Monthly 2013-08-02 13:21 https://vpetersson.com/2013/06/15/my-presentation-from-pi-and-more-3/ 20% Monthly 2013-06-16 09:03 https://vpetersson.com/2013/06/01/join-me-on-pi-and-more-on-june-15/ # Generating redirects Next, let’s do put work some Python magic. Thanks to [pytumblr](https://github.com/tumblr/pytumblr), this part is a breeze. Start by fetching [this](https://gist.github.com/vpetersson/83da37e80702078e7775) file. You need to update the following: - tumblr_blog - wordpress_site - source_file - `` - `` - `` - `` With that done, you should be able to simply run the script. The script should spit out redirect rules that you can install on your Nginx configuration. After that you’re done. Happy hacking! --- # My presentation from Zadara Summit URL: https://vpetersson.com/2014/07/24/my-presentation-from-zadara-summit/ Date: 2014-07-24 Last month I spoke at [Zadara Summit](http://zadarasummit2014.conference-websites.co.uk/). I did however, I did forget to post the speaker deck here, so here we go. My presentation deck is available [here](https://speakerdeck.com/vpetersson/cloudsigma-zadara-summit). --- # Installing Windows 7 on a MacBook Air URL: https://vpetersson.com/2013/12/08/installing-windows-7-on-a-macbook-air/ Date: 2013-12-08 It’s blasphemy, I know. Why would anyone in their right state of mind do such thing? Unfortunately, people outside the tech-world, and ISV who provides them with software, are still stuck with Windows (due to ignorance or lack of choice). If you want to equip someone in that world with a great and light laptop, a MacBook Air is a pretty good choice. It’s affordable, and since you will be installing a **pure** (i.e. non vendor/bloatware infected version) of Windows, it makes a pretty good Windows machine (an oxymoron, I know). In the process of preparing this MacBook Air, I was pretty close to going insane. To save you the agony I had to go through, here are the steps you need to take. - Make sure you have the following available: A Windows 7 installation DVD, a USB DVD drive, and a 8GB USB stick. - Insert the 32 or 64 bit version of Windows 7 into the USB DVD. - Fire up Disk Utility. Select the volume under the DVD drive. Click ‘New Image’ -> Select ‘DVD / CD Master’ as the image format and save it to disk. - Once the disk has been created, you need to convert it to an ISO image. This can be done using the Terminal by executing this command ‘hdiutil makehybrid -iso -joliet -o win7.iso win7.cdr’ (assuming you named the master image ‘win7.cdr’ and you’re in the directory you saved the image. - This next step is **very important**. You need to download [Caffeine](http://lightheadsw.com/caffeine/). This will prevent your machine from falling asleep during the Boot Camp Assistant run, which will take a while. Make sure you start Caffeine and that it is activated. If you fail to do this, your installation is most likely going to fail. You could of course disable all power saving features in OS X, but I find just running Caffeine faster. - Launch Disk Utility again and make sure you don’t have a Windows partition created from a prior installation attempt. If you do, make sure to delete it before continuing. If not, Boot Camp Assistant will behave differently. - Insert the USB stick **directly** into the laptop (i.e. don’t use a USB hub, as this can confuse the boot process later). Some people at various forum claims that it must be in the left USB port, but I’m not sure if that is true or not. - Launch Boot Camp Assistant. You need to make sure that you both create the USB drive **and** launch the installer. If not, the installation is most likely going to fail. You also need to select the ISO image we created earlier when the tool asks for the disk image. - Assuming you don’t run into any hiccups, you should be taken directly into the Windows installation. - After the installation, 12 hours of downloading security updates and 32 reboots, you should be up and running with your brand new Windows 7 machine. These notes will hopefully save you a bunch of hours, which I had to waste while learning these caveats. --- # Are They Using Google Apps? URL: https://vpetersson.com/2013/08/02/are-they-using-google-apps/ Date: 2013-08-02 In the past few months, I’ve started moved most of my conference calls from Skype to Google Hangout. The video and audio quality is a lot better, and it’s more convenient to manage. There is however one problem: If the other party isn’t using Google Apps or Gmail, they cannot join the Hangout. This has delayed many conferences calls for me. To resolve this issue, I wrote a simple web app called [Are They Using Google Apps?](http://aretheyusinggoogleapps.lab.vpetersson.com/). You can enter either the domain, or the email address, and the tool will tell you if the other party is using Google Apps (by simply checking the MX records). If they are using Google Apps, I will schedule a Google Hangout, and if not, I will schedule the conference call with [UberConference](http://www.uberconference.com/). --- # My presentation from Pi and More 3 URL: https://vpetersson.com/2013/06/15/my-presentation-from-pi-and-more-3/ Date: 2013-06-15 I just finished delivering presentation from [Pi and More 3](http://www.piandmore.de/) in Trier, Germany. Here’s the slide deck. **Update:** As it turns out, the presentation was also recorded. You can find the video [here](https://www.youtube.com/watch?v=uld1nRJtP_o&feature=player_detailpage#t=1694s). --- # Join me on Pi and More on June 15 URL: https://vpetersson.com/2013/06/01/join-me-on-pi-and-more-on-june-15/ Date: 2013-06-01 On June 15 I will be speaking at Pi and More in at Trier University in Germany. The event is all about Raspberry Pi, and I’m really excited to be part of the event. My presentation will be about Screenly and how we built the most popular digital signage solution for the Raspberry Pi thanks to the awesome Raspberry Pi community. You can read more about the event [here](http://www.piandmore.de/) (German). The event is free to attend, and you can sign up [here](https://piandmore3.eventbrite.de/). See you there! --- # Major update to Screenly URL: https://vpetersson.com/2013/02/21/major-update-to-screenly/ Date: 2013-02-21 ![Screenly_OSE_overview](https://vpetersson.com/wp-content/uploads/2013/02/Screenly_OSE_overview-600x432.png) I’m excited to announce that we’ve released a major update to Screenly. As you can see in the screenshot above, we’ve given the user interface a major overhaul. The workflow is significantly more streamlined, and a lot prettier. You can take the new interface for a spin [here](http://ose.demo.screenlyapp.com). For upgrade instructions, please take a look [here](http://www.screenlyapp.com/ose.html#upgrade). More information about Screenly is available [here](http://www.screenlyapp.com). --- # The world's shittiest monitor: Acer S235HLBII 23" URL: https://vpetersson.com/2013/02/19/the-worlds-shittiest-monitor-acer-s235hlbii-23/ Date: 2013-02-19 Ok, this a long rant, but I just need to warn anyone else looking to buy this monitor. It’s the worst piece of garbage I’ve ever owned. If you’re thinking about buy one, just don’t. Pretty much anything else you can get your hands on is better. tl;dr On paper, it’s a pretty decent monitor. It’s a regular 23″ monitor with 2x HDMI input and one VGA. It seemed pretty ideal, since I was primarily looking to use it for my Raspberry Pi labs. For me, monitors are pretty disposable, and I don’t pay much attention to them these days. This monitor however must have been created by a bunch of either extremely incompetent engineers, or skilled engineers with an extreme hate for the human race. For instance, the input detector is so shitty that when you plug in a regular computer to it, you’ll be way into the operating bootup before it even detects the monitor (if at all). That means, there’s no way in you’ll be able to do access the BIOS with this monitor (unless you know the exact key combinations (and no, it’s not just ‘Delete’ as it used to be in the good old days)). When using this monitor with the Raspberry Pi, things gets way worse. In order for this piece of garbage to detect the Raspberry Pi, you need to carefully time when you power up the Raspberry Pi compared to the power up cycle for the monitor. If you either power up the Raspberry Pi too early, or too late, it won’t detect it at all and go to sleep. Then there’s the monitor’s menu system. You’d imagine that a feature like switching between inputs on a monitor with three inputs would be pressing one key. Nope, on the Acer S235HLBII, you need to dive into the menu and dive into a submenu to do this. Moreover, **you can’t even get to the menu if the monitor can’t detect any input**. If you could at least force the monitor to a given input, I guess I could live with this monitor, but you can’t. It will automatically jump back and fort in order to its own pathetic probing. For the above reasons, I want to congratulate Acer for designing the world’s shittiest monitor. --- # Introducing Puppet-hosting -- host websites with sanity URL: https://vpetersson.com/2013/02/03/introducing-puppet-hosting-host-websites-with-sanity/ Date: 2013-02-03 Since we started WireLoad, the number of websites we host have grown steadily. It’s a myriad of sites, ranging from product sites to websites belonging to friends and family. Many of them are WordPress-sites (just like this one), while others are more complicated. Some use databases, while others don’t. Since we’re often in a rush when we set up a site, documentation often suffers, and you have to spend time later on trying to decipher how things were set up. This past week I finally had enough and decided to resolve this issue once and for all. What we really needed was a template-based system that could take care of everything and provide us with a user friendly interface. Puppet felt like the best tool for the job so I got busy writing a custom module for this. The final result is a module I named [Puppet-hosting](https://github.com/vpetersson/puppet-hosting). It allows you to manage all your websites using Puppet. To add a new site, all you need to do is to add a few lines to in your site.pp, and you’re all set. Here’s an example of how it can look: hosting::site { 'My Website': type => 'wordpress', url => 'mysite.net', url_aliases => ['mysite.*', 'www.mysite.net'], ip => $ipaddress_eth0, contact => 'Viktor Petersson ', } Not only does this make it much easier to manage all the sites, it also speeds things up and avoids human errors, such as typos (and if you do have a typo, it’s easy to spot, since it’s only a few lines in sites.pp). --- # Screenly now has its own website URL: https://vpetersson.com/2013/01/22/screenly-now-has-its-own-website/ Date: 2013-01-22 I’m astonished by the amount of traction we’ve been seeing for Screenly. The Open Source-version is growing rapidly in traction, while the wait-list for the Pro-version is growing. Given the amount of traction, we’ve now allocated more resources from WireLoad towards Screenly. The first priority right now is a cleanup of the code base and to improve the user interface for Screenly OSE. Since I’ve been receiving a lot of emails from the contact form on my website about Screenly, we felt that it was important to push out a website for Screenly. We’ve now done this, and you can find it at [ScreenlyApp.com](http://www.screenlyapp.com/). On the website, you’ll find more information about Screenly, along with a live-demo of Screenly OSE. --- # Cloud lifecycle: How to deal with decommissioned nodes URL: https://vpetersson.com/2013/01/14/cloud-lifecycle-how-to-deal-with-decommissioned-nodes/ Date: 2013-01-14 There’s no doubt that virtualization and the cloud is here to stay. So you migrated your entire architecture to the cloud and everyone is happy. Eventually, you’ll come to a point where you start decommission servers. If this was an on-premise server, all you had to do was to powered it off and perhaps put it to use elsewhere (or if virtualized, simply delete it). In the cloud however, it’s tempting to do the same. What people don’t think about however is that most cloud vendors use regular magnetic disks. This means that when you delete a virtual drive, it will be provisioned to someone else. Normally, the first thing the next person who gets provisioned your old disk blocks (or parts of it) would do is to format it and fill it with data. **However**, if this person is a malicious user, s/he could restore what was written to those disk blocks, just as s/he could with a magnetic drive that has been formatted. Therefore, before I decommission any drives in the cloud, this is what I do: - Power off the system - Change the boot device to a Live CD (most linux-distributions will do) - Run [shred](http://manpages.ubuntu.com/manpages/intrepid/man1/shred.1.html) on the device - Power off the system and delete the drive While shredding the drive will take a fair amount of time, we know that even if a malicious user is provisioned the same disk blocks, they won’t find any of your data. --- # Axis M1114 + mplayer = Win URL: https://vpetersson.com/2013/01/09/axis-m1114-mplayer-win/ Date: 2013-01-09 I’m a long-time fan of Axis’ IP cameras. I’ve played with a few other IP cameras too, but I’ve never come across a camera with the performance and stability of Axis’ products. Recently I deployed two [Axis M1114](http://www.axis.com/products/cam_m1114/)-cameras. It’s an impressive device with built in motion sensor (that can record to a SMB or FTP) with Power over Ethernet (PoE). The optics in the device is equally impressive, as it capable of recording data in down to 0.6 Lux. Once I had deployed the cameras, I wanted to view the feeds on two statically mounted monitor. With the help of two spare monotors and an Asus EEEPC (a low-powered Atom-based computer with both HDMI and VGA output) I was able to do just that. A simple Ubuntu installation with auto-login did the trick. The secret sauce however was mplayer. Since this is a low-powered computer with a rather powerful GPU (Nvidia ION), I had to offload the rendering to the GPU to avoid overheating. After installing the required GPU drivers and configure both monitors, all I had to do was to create two scripts and have them launch at boot: **cam0.sh** #!/bin/bash sleep 5 while [ true ] do mplayer -nosound -display :0 -quiet -vo vdpau -vc ffh264vdpau -fs rtsp://cam0/axis-media/media.amp?videocodec=h264 -rtsp-stream-over-tcp -hardframedrop sleep 2 done **cam1.sh** #!/bin/bash sleep 5 while [ true ] do mplayer -nosound -display :0.1 -quiet -vo vdpau -vc ffh264vdpau -fs rtsp://cam1/axis-media/media.amp?videocodec=h264 -rtsp-stream-over-tcp -hardframedrop sleep 2 done Simple as pie! Even with two streams, the computer is at 99% idle. Also, even if the power goes out, the computer will automatically boot up and show the stream. If you have different hardware, you might need to adjust the ‘-vo’ and ‘-vc’ variables to fit your hardware. --- # HP ProLiant-hacking. Powerful rack server on the cheap. URL: https://vpetersson.com/2012/12/22/hp-proliant-hacking-powerful-rack-server-on-the-cheap/ Date: 2012-12-22 Rack-servers make your life easier. They allow you to stack a bunch of servers into a small area while still keeping them well organized. Unfortunately, most server-vendors know this, and will charge you an arm and a leg for them. I recently had to build up a small virtualization-farm, and I really wanted to keep all server in a rack without having to spend a fortune on servers. Enter [HP ProLiant DL-120](http://h10010.www1.hp.com/wwpc/us/en/sm/WF06b/15351-15351-3328412-241644-3328421-5075933-5173493-5173494.html?dnr=1). It might not look too impressive, but for the price-point (I paid ~$800 each), it’s not bad 1U server. However, we haven’t started with beefing it up. Photos of the setup can be found [here](https://plus.google.com/photos/102112347693505491575/albums/5824880631135958625?authkey=CNeWsJ6r67PABQ). ### The extras. The first thing you may notice is that the server ships without hard drives. HP want you to buy their ridiculously overpriced server-disks (which are basically regular SATA/SAS drives in a plastic enclosure). I opted out for this and went for regular drives (and I couldn’t find any compatible DIY plastic enclosures). Since we’re not using these enclosures, this means that we won’t be able to hotswap drives unfortunately. Personally I think that was a reasonable compromise. It also turns out that the on-board RAID-controller is just a crappy software controller that performs poorly under Linux. To remedy these shortcomings, I added a few extra things to my shopping list: - 4 x 4 GB RAM (I went for HP’s, as they weren’t too overpriced) - 4 x 2TB SATA 7200 RPM drives - 1 x Adaptec 2405 - 2 x SATA extenders (0.5m) - Optional: USB CD/DVD reader (for installation) With these extensions, we’re able to create a pretty beefy server with 16GB RAM and 2TB of usable storage backed with (hardware) RAID 10. ### What you’re giving up. First and foremost, you’re giving up the ability to hotswap drives by opting out for the HP’s stock-hard drives. This also means that you won’t have any HDD leds in the front. While I would much liked that, we’re a lot of extra disk space in return. Yet, all-in-all, I’m willing to give that up to save a few hundred dollars per server. **Putting it all together.**\ Once you have all the parts, it’s time to put it all together. It shouldn’t be too hard, but it is a bit hackish. A few things you’ll notice is that you need to put the Adaptec-card into the full-height slot, otherwise the cables won’t reach. With the Adaptec-card installed, you can connect the SATA cables to the front by following the existing cables. The built-in cable should be sufficient for the first two drives, but you need the SATA extension cables to reach drive three and four. To install the hard drives, you first you need to remove all of the plastic placeholders in the front. Then insert the hard drives upside down. This will allow you to connect the drives to the Adaptec-card as well as power. Once installed, simply secure the drives with some tape in the front. Installing the memory should be very straight forward. ### Initialize the RAID array. This one took me some time to figure out. While booting up, the HP’s BIOS will display “Press any key to view Option ROM messages.” I didn’t really connect the dots here, but this is what you need to do to get to Adaptec’s BIOS. ### Wrapping up. Now, with the RAID initialized, all you need to do is to add your USB cd/dvd drive and boot into your favorite Linux distribution, which should detect your hardware without any extra drivers. --- # My presentation deck from gSocial URL: https://vpetersson.com/2012/11/14/my-presentation-deck-from-gsocial/ Date: 2012-11-14 Yesterday I delivered a speech at the gSocial. If you haven’t heard of gSocial, it is the largest independent event for the Google Apps Channel Community. My talk was about email migration, and what we have learned since launching YippieMove back in 2008.

--- # Screenly Pro is now in beta URL: https://vpetersson.com/2012/10/16/screenly-pro-is-now-in-beta/ Date: 2012-10-16 A while ago I started hacking on Screenly, a digital signage solution for the Raspberry Pi. It has been a huge success with a lot of traction from the Raspberry Pi community. The open source edition of Screenly (a.k.a. Screenly OSE) is an excellent tool if you want to setup a single sign. All you need is a Raspberry Pi ($35), some cables, and a monitor. You can literally be up and running in five minutes (not including the time it takes to flash Rasbian to the SD-card). But what if you want to manage a number of nodes? Let’s say you have one hundred nodes that you want to manage from place? The answer to that is Screenly Pro. It’s based on the same platform as Screenly OSE, but it enables you to easily manage a large number of nodes. It also comes with a number of other improvements to make it easier to setup.\ ![Screenly Pro](https://vpetersson.com/wp-content/uploads/2012/10/screenly_pro-600x419.png "Screenly Pro")\ We already have a fully working beta of Screenly Pro running, but we are now taking ready to start letting new users in. If you’re interested, please [sign up](http://signup.screenlyapp.com/) for the wait-list and we’ll let you know as soon as we’re ready to ship. --- # Join me at gSocial 2.0 URL: https://vpetersson.com/2012/10/14/join-me-at-gsocial-2-0/ Date: 2012-10-14 ![](https://vpetersson.com/wp-content/uploads/2012/10/gSocial-NEW.jpg "gSocial")\ [gSocial](http://gsocialconference.com/) is a conference for Google Apps resellers and ISVs. I attended the event last year, and I was really impressed. I’m back for the conference this year, but I will aslo be giving a talk. My talk will be on the topic of email migration and what we’ve learned over the years working with [YippieMove](http://www.yippiemove.com/). The event also happens to have great timing, since we are just about to release YippieMove’s API, and we’d love talk with the bright people at gSocial about it. The schedule for the event is available [here](http://gsocialconference.com/#57e/custom_plain), and you can register [here](https://events.r20.constantcontact.com/register/eventReg?oeidk=a07e6ckv9sj2f25a775&oseq=). --- # My presentation deck from NoSQL Roadshow (Basel, Switzerland). URL: https://vpetersson.com/2012/08/30/my-presentation-deck-from-nosql-roadshow-basel-switzerla/ Date: 2012-08-30

--- # Featured in SCU's Alumni Entrepreneur Spotlight URL: https://vpetersson.com/2012/08/23/featured-in-scus-alumni-entrepreneur-spotlight/ Date: 2012-08-23 A few weeks ago, I was contacted by [SCU](http://scu.edu/)‘s Center for Innovation and Entrepreneurship (CIE). Apparently I was mentioned in [Santa Clara Magazine](http://www.scu.edu/scm/summer2012/index.cfm) about being featured on TechCrunch. CIE wanted me to write an article about my background and my experience as an entrepreneur. I was happy to share that with them, and the [article](http://www.scu.edu/business/cie/alumni_and_friends/Viktor-Petersson.cfm) went live today. --- # Complete refactoring of 'csconnect' URL: https://vpetersson.com/2012/08/19/complete-refactoring-of-csconnect/ Date: 2012-08-19 A few months back I wrote [csconnect](https://github.com/vpetersson/csconnect) to make it easier for myself to logon to our Cloud Sigma-nodes. It’s a pretty simple application. All it does is to poll Cloud Sigma’s API and lookup the IPs for the node(s) specified. Yet, since this was one of the first Python-programs I ever wrote (beyond Hello World), the state of the code was pretty terrible. Last night I took on myself to rewrite the program from the ground up. It works more or less the same. The only differences are that: - I feel a lot better knowing the code is prettier :) - It now uses a config-file to host the Cloud Sigma-credentials (instead of being stored in the actual script) You can grab csconnect from its [Github-repo](https://github.com/vpetersson/csconnect). --- # Fixing the broken Munin Nginx-plugins in Ubuntu (optional: with Puppet) URL: https://vpetersson.com/2012/08/16/fixing-the-broken-nginx-plugins-in-ubuntu-optional-with/ Date: 2012-08-16 I love Munin. It’s a great monitoring tool, quick to set up, and doesn’t come with too many bloated requirements. It’s also very flexible and easy to write plugins for. On Ubuntu, Munin comes with most (non-custom) plugins I use. Unfortunately, the Nginx-plugins (nginx\_status and nginx\_request) are incorrectly documented. In the header of the plugins, one can read that the default URL is `http://localhost/nginx_status`, which certainly makes sense. The plugin even documents how one were to set this up. **However**, the plugin logic tells a different story. In nginx_requests, we can see that it relies on ‘hostname -f’ to look up the hostname that it connects to (which in 99.999% of all production servers isn’t ‘localhost’). The fix however, is very easy. All you need to do is to add the URL to the Munin’s plugin configuration file. This can be done using the following command: if [[ $(cat /etc/munin/plugin-conf.d/munin-node | grep "nginx") = "" ]]; then echo -e "\\n\[nginx*\]\\nenv.url http://localhost/nginx_status" >> /etc/munin/plugin-conf.d/munin-node; fi This can be run as many times as you’d like, as it only appends the config-snippet if it’s not there already. In my case, I wanted to push this out using Puppet, so I have the following block in my Puppet Munin-module: package { 'munin-node': ensure => 'present', } service { 'munin-node': ensure => 'running', hasrestart => 'true', hasstatus => 'true', enable => 'true', } file { 'nginx_request': ensure => 'link', path => '/etc/munin/plugins/nginx_request', target => '/usr/share/munin/plugins/nginx_request', require => Package\['munin-node'\], } file { 'nginx_status': ensure => 'link', path => '/etc/munin/plugins/nginx_status', target => '/usr/share/munin/plugins/nginx_status', require => Package\['munin-node'\], } # Fixes a bug in the plugin and configures it to poll using localhost exec { 'activate\_nginx\_munin': command => 'bash -c if \[\[ $(cat /etc/munin/plugin-conf.d/munin-node | grep "nginx") = "" \]\]; then echo "\\n\[nginx*\]\\nenv.url http://localhost/nginx_status" >> /etc/munin/plugin-conf.d/munin-node; fi', user => 'root', require => \[ File\['nginx_status'\], File\['nginx_request'\], \], path => \[ '/usr/sbin', '/usr/bin', '/sbin:/bin', \], } (I also use a Puppet-template for Munin’s config-file, but that’s beyond the scope of this article.) --- # Time Machine on Mountain Lion URL: https://vpetersson.com/2012/08/15/time-machine-on-mountain-lion/ Date: 2012-08-15 ![](https://vpetersson.com/wp-content/uploads/2012/08/Time-Machine.png "Time Machine") I’m not sure if I’m the only one having issues with Time Machine, but it has been completely broken for me since I upgraded to Mountain Lion. As you can see above, Time Machine basically freezes and reports outrageous ETA statistic. What is strange is that I did a fresh install. I do have FileVault2 activated on both the Time Machine-drive (which is brand new) and my internal drive. That should work fine, as I used the same setup on Lion. At this point, I’m just waiting for 10.8.1 to be released (the developer preview is already out) and hoping that it will solve the issue. Other than that I’m out of ideas. I’ve formatted the Time Machine drive a few times just to ensure that it wasn’t a filesystem issue. Heck, I even ‘securely’ formatted it and wrote zeros all over the drive just to be safe, but still no change. The one other thing that I’m thinking may be related is that the external drive used is a USB3-drive. Perhaps that is causing trouble for my MacBook Pro (MacBookPro8,2). **Update**: Looks like i’m [not alone](https://discussions.apple.com/thread/4145494?start=60&tstart=0) with having this issue. **Update 2**: After struggling with this annoying issue for a few weeks now, I decided to try using my NAS as the Time Machine-target. Since you can encrypt your remote backups, I thought I’d give it a shot. While it started out fine and copied about 1.5GB of data, it then stalled. I guess this proves that there weren’t anything wrong with my USB-drive and that the issue is with Time Machine’s engine. **Update 3**: After a reboot and running ‘Repair disk permission’ (in ‘Disk Utility’) on the system drive, I was able to successfully backup my drive on the NAS. I hate to not be able to share a solution to all the other people who were having the issue, but it seems to work for me. As much as I’d love to see if it also works with the USB-drive, I do not want to jeopardize anything right now. **Update 4:**: It was probably unfair to blame this on Apple. It turns out that my SSD was giving up on me. Unlike magnetic drives, there was no physical way to tell. No clicking noises, and no obvious write errors. Instead the disk appears to have failed silently and caused Spotlight’s indexing to fail. That in turn caused Time Machine to fail. I’ve now switch out the drive for a brand new one, and the issues appears to have gone away. --- # My latest project: Screenly URL: https://vpetersson.com/2012/08/01/my-latest-project-screenly/ Date: 2012-08-01 One of the most hyped pieces of hardware in recent time is the [Raspberry Pi](http://www.raspberrypi.org/). It is hyped for a very good reason. For $35 you get a fully functioning Linux-computer which is about the size of a deck of cards. While there have been plenty of micro-computers around in the past, none at this extremely low price point. The Raspberry Pi is simply any DIY’ers wet dream. After months of wait, I finally got mine a few weeks back. My first project was **Screenly: A digital signage platform built for the Raspberry Pi**. While still in early beta, the Screenly is fully functional and [available on Github](https://github.com/wireload/screenly-ose) for anyone to download and install. The entire system is designed to be a ‘set and forget’-appliance. Once installed, you manage the content from your web browser on your local computer. Here’s a video of an early version of Screenly in action: There are also screenshots and photos available of Screenly [here](https://picasaweb.google.com/102112347693505491575/Screenly01?authkey=Gv1sRgCNa2qp-j5vWUGQ). For more information about Screenly, please visit the [Github-project](https://github.com/wireload/screenly-ose). --- # A permanent fix for Apple's "play/pause"-hijacking (and use Spotify) URL: https://vpetersson.com/2012/07/14/a-permanent-fix-for-apples-playpause-hijacking-and-use-s/ Date: 2012-07-14 I hate iTunes with passion. It’s bloated and annoying. However, since it is a major revenue stream for Apple, they try hard to push it into your face as often as possible. Since I started using Spotify a few years back, I don’t think I’ve used iTunes to play music once (only to upgrade iOS-devices before the over-the-air-updates came out). As long as you don’t touch any other Apple products, Spotify will take over the media control-keys, but as soon as you start one of Apple products (like Keynote or Quicktime), Apple hijacks the media control-keys. It’s beyond frustrating. Instead of starting (or pausing) the music in Spotify, iTunes pops up and it makes me furious every time. Fortunately, I just found a solution. It isn’t perfect, but it’s better than anything else I’ve found. - Start by going to “System Preferences” -> “Keyboard” and select “Use all F1, F2, etc. keys as standard function keys” - Next, download the application [Shortcuts](http://itunes.apple.com/us/app/shortcuts/id402271673?mt=12) from Mac App Store Within Shortcuts, you now need to create two Apple Scripts: one for play/pause, and one for next track. The Play/Pause script looks like this: tell application "Spotify" to playpause And the ‘Next Track’ looks like this: tell application "Spotify" to next track Now simply just bind these scripts to your Play/Pause button (F8) and Next button (F9). ![](https://vpetersson.com/wp-content/uploads/2012/07/Shortcuts-600x413.png "Shortcuts") **Note**: I’m not using the regular F8/F9 mapping, as I’m not using an Apple keyboard. There are three drawbacks with this approach unfortunately. First, it is hardcoded to Spotify. That’s fine with me, as that’s the only media player I really use the media control keys for. The second drawback is that you need to access all the non-function keys using FN+\[the key\]. The third drawback is that you need to have the Shortcuts-application running at all times. For me these are reasonable compromises, but a compromises nonetheless. In a perfect world, Apple would just stay away from hijacking the keys. --- # Join me at NoSQL Roadshow in Basel! URL: https://vpetersson.com/2012/07/10/join-me-at-nosql-roadshow-in-basel/ Date: 2012-07-10 ![](https://vpetersson.com/wp-content/uploads/2012/07/nosql-basel-2012-600x109.png "nosql-basel-2012") If you have nothing booked for August 30th, I suggest you mark it ‘busy’ in your calendar, as that’s the date for [NoSQL Road Show](http://nosqlroadshow.com/) in Basel, Switzerland. At the event, I will be giving a presentation about MongoDB titled “[MongoDB’s Replica Sets – Painless scaling and High Availability (HA)](http://nosqlroadshow.com/nosql-basel-2012/presentation/MongoDB's%20Replica%20Sets%20-Painless%20scaling%20and%20High%20Availability%20(HA)).” As the title implies, it is a talk about MongoDB’s Replica Sets. I will demonstrate how easy it is to setup, and also talk about lessons I’ve learned using MongoDB. Register for the event [here](https://secure.trifork.com/nosql-basel-2012/registration/). --- # Access control in Bottle (by IP) URL: https://vpetersson.com/2012/06/26/access-control-in-bottle-by-ip/ Date: 2012-06-26 If you haven’t heard of [Bottle](http://bottlepy.org/docs/stable/), it’s a lightweight web framework for Python. It is perfect if you have a small project that requires a web interface, but you don’t want to go all in with a complex framework like Django. Since Bottle is so lightweight, it doesn’t always have all the features you need built-in. One thing that I was missing was access control. For instance, what if you want to limit access to an admin-page to a certain IP? Sure, if you’re running you’re app behind a full-fledge webserver like Nginx or Apache, you can use it to limit access, but that doesn’t work if you’re deploying to something like [Heroku](http://www.heroku.com/). As it turns out, implementing a feature like this yourself isn’t really that hard. We’ll simply rely on the HTTP flags REMOTE\_ADDR and HTTP\_X\_FORWARDED\_FOR. Just checking for REMOTE_ADDR won’t work on Heroku. First, start by creating a function that checks for this: def adminAccess(): remoteaddr = request.environ.get('REMOTE_ADDR') forwarded = request.environ.get('HTTP\_X\_FORWARDED_FOR') if (remoteaddr in accessList) or (forwarded in accessList): return True else: return False Next, create a list of IPs that have access to the admin pages: accessList = ["123.123.123.123"] Now, all you need to do is to add a check for this on each page you want to restrict access. For instance here’s a pointless admin-page that checks for your IP: @route('/admin') def admin_page(): if adminAccess(): pass else: return "Access denied" return "Yay! It worked!" That’s it. Pretty straight forward. --- # Munin-plugin for Zendesk URL: https://vpetersson.com/2012/06/23/munin-plugin-for-zendesk/ Date: 2012-06-23 In recent time, I’ve really started to appreciate Munin. I’ve deployed Munin in multiple architectures already, and I still get impressed every time by how easy it is to setup. I also really like how easy it is to write plugins. For a crash-course in writing plugins for Munin, take a look at [this](http://munin-monitoring.org/wiki/HowToWritePlugins) page. Since I first deployed Munin to monitor [YippieMove](http://www.yippiemove.com)‘s architecture, I’ve written a handful of custom plugins to visualize various datapoints. However, one thing I’ve been wanting for some time was to a tool to visualize the volume of support tickets. Since we use [Zendesk](http://www.zendesk.com/) for support and the fact that they already got an API, all data I needed was already accessible. I started writing the plugin this morning, and few hours later I had written one plugin for plotting all tickets (with their state), and another one for customer satisfaction. If you want to take it for a spin, I’ve published it on [Github](https://github.com/vpetersson/munin_zendesk). --- # New project: csconnect.py URL: https://vpetersson.com/2012/05/15/new-project-csconnect-py/ Date: 2012-05-15 In the last few years, [we](http://wireload.net)‘ve spent a lot of time migrating away from all our physical servers and into the cloud. This has been a very interesting task, that presented its own set of challenges, but it has certainly been worth it. One of the issues though with working in a public cloud environment is that you don’t necessarily have the same static IP configuration as you do with dedicated hardware. When you power off a node, and spin it back up (or clone a new server for that sake), it’s likely that it will switch IP. This is at least the case with CloudSigma, which is what we are using for a large part of our server needs. Normally, you’d have to login to CloudSigma’s web-interface to find out the IP for a given node. Needless to say, this gets old pretty fast when you quickly want to SSH into a node. To resolve this, I wrote [csconnect.py](https://github.com/vpetersson/csconnect) This little handy script connects to CloudSigma’s API and resolves your IPs. It even uses a local cache of your servers IPs for quick lookups. --- # Deploying FreeBSD's Ports to a large number of nodes URL: https://vpetersson.com/2012/05/07/deploying-freebsds-ports-to-a-large-number-of-nodes/ Date: 2012-05-07 Some time ago, I posted a [question](http://serverfault.com/questions/364556/how-do-you-manage-and-deploy-freebsds-ports-in-a-large-environment) on Serverfault about how people managed and deployed ports in a large environment. Despite a fair number of comment, nobody seemed to really have the anser to this (or at least that suited my needs). It simply appears to be the case that the built-in tools in FreeBSD (primarily portsnap and portupgrade) are not built for a shared ports-tree. Having a local ports-tree on each node seemed both wasteful and inefficient to me, yet this what was I had to resort to. With that decided, how do you optimize the setup given this new setup? ### Distribute packages The new setup won’t require any special configuration of either portsnap or ports itself (unlike my previous approach discussed in the Serverfault-post). That said, we do however want to utilize NFS for sharing the folder ‘/usr/ports/packages.’ If we don’t do that, we won’t be able to build and distribute packages to be used with portupgrade easily. ### Use a caching proxy Since portsnap simply talks over the HTTP-protocol (via ‘fetch’), we can utilize a caching proxy. This will both offload the official servers and speed up the process. To accomplish this, just set up Squid on one of your servers. Just make sure you increase the caching size to something like 500MB on disk to make sure that all the files fit in the cache. With Squid up and running, simply run the following on each node: **On Csh:** setenv http_proxy http://myserver:3128 **On Bash:** export http_proxy="http://myserver:3128" While this is still not as KISS as simply exporting the ports-tree, it is better than just having a stand-along setup on each node. Also, keep in mind that both Squid and your NFS server are SPOFs. For a production environment, consider adding failover to both. --- # Monitor Memcached with Munin (on Ubuntu) URL: https://vpetersson.com/2012/04/14/monitor-memcached-with-munin-on-ubuntu/ Date: 2012-04-14 Let me first admit that I am new to Munin. I’ve played around with most monitoring tool, but never Munin for some reason. I really don’t know why, since it appears to be a great tool. As a result, this might be obvious to seasoned Munin-users, but it wasn’t to me at least. It appear as most stock-plugins are configured in _/etc/munin/plugin-conf.d/munin-node_. This isn’t the case for the Memcached plugin. Hence this post. Start by grabbing the Memcached plugin from [here](https://github.com/munin-monitoring/contrib/tree/master/plugins/memcached). Copy these files to _/usr/share/munin/plugins_. Next, install the required Memcached-perl plugin: sudo apt-get install libcache-memcached-perl Now to the strange part. In order for this plugin to work, we need to include the hostname and the port of Memcached in the filename. We do this we create a symlink to the plugin that includes this info (eg. `memcached\_traffic\_127\_0\_0\_1\_11211` if Memcached is listening on 127.0.0.1:11211). Since I deployed this on a few servers, I wrote a simple shell-script that does this: #!/bin/bash for i in $(find /usr/share/munin/plugins/memcached*); do ln -s $i /etc/munin/plugins/$(basename $i)127\_0\_0\_1\_11211 done After you’ve installed the plugin, let’s make sure it worked. #!/bin/bash for i in $(find /etc/munin/plugins/memcached*); do sudo munin-run $i done Assuming you didn’t get any errors when running the script above, go ahead and restart Munin-node (service munin-node restart). **Update**: It appears as this is a pretty common approach to pass variables to Munin-plugins. --- # I'm interviewed by City Network URL: https://vpetersson.com/2012/03/24/im-interviewed-by-city-network/ Date: 2012-03-24 This week I was [interviewed](http://www.citynetwork.se/kunder/wireload-irriterande-bra-mjukvara-och-tjanster/) (Swedish) by the Swedish hosting provider City Network for their blog. In the article I talk about the background of [WireLoad](http://wireload.net) and a bit about our network architecture and philosophy. --- # How to quickly slice wallpapers for a dual-screen setup URL: https://vpetersson.com/2012/03/09/how-to-quickly-slice-wallpapers-for-dual-screens/ Date: 2012-03-09 In this day and age, many people use multi screens in their setup. I’m one of those people. One thing that really bugs me in OS X however, is that you cannot set one wallpaper to expand beyond one screen. Let’s say you have two screens with 1920×1080 resolution and you have found a wallpaper of the appropriate dimension (ie. 3840×1080). [Interfacelift](http://interfacelift.com/wallpaper/downloads/date/2_screens/1920x1080/) got plenty of them. Now, in order to properly use these, you need to slice the image into two images (one for each screen). Sure, you can do this in Photoshop, but that feels like an overkill. Instead, let’s use the good ‘ol tool [ImageMagick](http://www.imagemagick.org/) (you can install it using [Homebrew](http://mxcl.github.com/homebrew/)). convert source.jpg -crop 1920x1080 sliced.jpg And voilá — you know got two images. One for each screen: - sliced-0.jpg - sliced-1.jpg That wasn’t very hard, was it? If you have screens of different resolutions, that is a bit trickier, but ImageMagick should still be able to do it. --- # The 'pg_ha' project has moved to Github URL: https://vpetersson.com/2012/02/27/the-pgha-project-has-moved-to-github/ Date: 2012-02-27 I recently posted a rather lengthy article titled “High availability with PostgreSQL, PGPool-II and FreeBSD.” The article was a bi-product of setting this up and the blog-post was simply my own notes with some polish. Little did I know when I started this that I would end up having to write this much code to make the system behave the way I wanted. While crafting the article, I decided that the most logical thing would be to create a Github repository with the scripts for easy maintenance. After publishing the article, I realize that the instructions I wrote really should be with the code. Hence, I have now moved the instructions over to the [Github wiki](https://github.com/vpetersson/pg_ha/wiki). Hopefully this will create more visibility to the project and I will have other people contribute to the project. --- # Comparing MongoDB write-performance on CentOS, FreeBSD and Ubuntu URL: https://vpetersson.com/2012/02/13/comparing-mongodb-write-performance-on-centos-freebsd-an/ Date: 2012-02-13 Recently I wrote a post titled ‘[Notes on MongoDB, GridFS, sharding and deploying in the cloud](https://vpetersson.com/2012/01/29/notes-on-mongodb-gridfs-and-sharding-in-the-cloud/).’ I talked about various aspects of running MongoDB and how to scale it. One thing we really didn’t take into consideration was if MongoDB performed differently on different operating systems. I naively assumed that it would perform relatively similar. That was a very incorrect assumption. Here are my findings when I tested the write-performance. As it turns out, MongoDB performs very differently on CentOS 6.2, FreeBSD 9.0 and Ubuntu 10.04. This is at least true virtualized. I tried to set up the nodes as similar as possible — they all had 2GHz CPU, 2GB RAM and used VirtIO both for disk and network. All nodes also ran MongoDB 2.0.2. To test the performance, I set up a FreeBSD 9.0 machine (with the same specifications). I then created a 5GB file with ‘dd’ and copied it into MongoDB on the various nodes using ‘mongofiles.’ I also made sure to wipe MongoDB’s database before I started to ensure similar conditions. For FreeBSD, I installed MongoDB from ports, and for CentOS and Ubuntu I used 10gen’s MongoDB binaries. The data was copied over a private network interface. I copied the data five times to each server (“mongofiles -hMyHost -l file put fileN”) and recorded the time for each run using the ‘time’-command. The data below is simply (5120MB)/(average of real time in seconds). The result was surprising to say the least. \[easychart type=“vertbar” height=“400” title=“MongoDB write-speed in MB/sec” groupnames=“FreeBSD 9.0, Ubuntu 10.04, CentOS 6.2” valuenames=“” group1values=“14.36” group2values=“27.25” group3values =“21.19”\] Ubuntu is almost 2x as fast as FreeBSD! I didn’t see that one coming. I’m not really sure why the difference is this great. I also think the fact that CentOS and Ubuntu used 10gen’s own build, and it might have some optimizations. Another thing that surprises me was the different between Ubuntu and CentOS, as they’re both using 10gen’s own binaries. It should also be mentioned that this test was performed in a public cloud, and the load from other systems could have impacted the performance. Yet, I ran a few tests a few days earlier, and came up with similar numbers. **Update:** I’ve been in touch with Ben Becker over at 10gen and he said that they’re not doing anything special with their binaries. They’re build in a VM with code fetched from the Github-repository without any changes. Hence, this difference is most likely explained by a less mature/optimized Virtio-driver in FreeBSD. --- # FreeBSD failover in the cloud -- UCARP to the rescue URL: https://vpetersson.com/2012/02/11/freebsd-failover-in-the-cloud-ucarp-to-the-rescue/ Date: 2012-02-11 I’m a big fan of FreeBSD. However, as painful it is to admit, it isn’t always the best OS to run in the cloud. Compared to Linux, you will get worse network and disk performance even with Virtio installed. There are also other issues. For instance, it is likely that you won’t get [CARP](http://www.freebsd.org/doc/handbook/carp.html) to fully work (while this works perfectly fine with OpenBSD’s CARP, and Linux’s VRRP). I have [written about workarounds](https://vpetersson.com/2011/03/23/how-to-get-freebsds-carp-working-on-cloudsigma/) for this issue in the past, but they do not seem to work equally well in FreeBSD 9.0. Luckily, there is a userland implementation of CARP called UCARP that works better than CARP. It’s also very similar to CARP when it comes to configuration. Unfortunately UCARP’s [website](http://www.ucarp.org/project/ucarp) includes more or less zero documentation, so I’ll help you get started. I won’t talk too much about what CARP is, as I assume you already know that if you’re reading this. There is however one major difference between CARP and UCARP — UCARP doesn’t have its own dedicated interface. Instead it relies on IP aliases that are brought up and down with the scripts below. First of all, you need to install UCARP from ports: cd /usr/ports/net/ucarp/ && make clean install We now need to enable UCARP in rc.conf by adding the following: ucarp_enable="YES" ucarp_if="vtnet1" ucarp_vhid="1" ucarp_pass="carppass" ucarp_preempt="YES" ucarp_facility="daemon" ucarp_src="192.168.10.10" ucarp_addr="192.168.10.1" ucarp_advbase="2" ucarp_advskew="0" ucarp\_upscript="/usr/local/bin/ucarp\_up.sh" ucarp\_downscript="/usr/local/bin/ucarp\_down.sh" I’m not going to go into too much details about the above variables. You can read more about them [here](http://www.freebsd.org/cgi/man.cgi?query=carp&sektion=4). You will however most likely need to change _ucarp_if_, _ucarp_src_, _ucarp_addr_, _ucarp_advskew_, and _ucarp_advbase_ to match your environment. They’re mostly self-explanatory. The only somewhat confusing ones I guess would be _ucarp_src_, which is the host’s IP and _ucarp_advskew_, which determines priority (the lower value will become master). Next, we need to create two script that will be triggered in the two different states. **ucarp_up.sh** #!/bin/sh # Load variables from rc.conf . /etc/rc.subr load\_rc\_config ucarp /sbin/ifconfig $ucarp\_if alias $ucarp\_addr/32 **ucarp_down.sh** #!/bin/sh # Load variables from rc.conf . /etc/rc.subr load\_rc\_config ucarp /sbin/ifconfig $ucarp\_if -alias $ucarp\_addr/32 With all of those files in place, you can simply start ucarp with: /usr/local/etc/rc.d/ucarp start ## Closing thoughts Once you’ve gotten the UCARP working, it’s time to tie it into your desired workflow. Contrary to CARP, which relies on dev.d for triggar-actions, you will use ucarp\_up.sh and ucarp\_down.sh. The biggest downside perhaps with UCARP is that you can’t bind an application on the failover IP when the node is in backup mode, like you can with CARP. As a result, you will need to work a bit harder when scripting your up/down actions. There is however one benefit with UCARP — you can configure it to run actions **prior** to bringing up the new interface. That can be handy if you need to do something before the node steps up as mater. --- # The story of Devify and Red iGone URL: https://vpetersson.com/2012/02/09/weve-open-sourced-red-igone/ Date: 2012-02-09 About two years ago I started working on a project called [Red iGone](http://www.redigone.com) together with a [friend](http://stefan.blomqwist.com/) and we started the company [Devify](http://devify.com/). The objective of the first project was simple — we wanted to make it easy to remove red eyes from photos. We didn’t see any good solution in the market place that was both fast and easy to use. Red iGone was born as a web-app and it did pretty well for itself. We got coverage from some semi-big blogs and traffic picked up. Our next goal was to enter the iOS market. At this point another [friend](http://pushforward.se/) joined the project to help out with the design. We soon had both an iPhone and an iPad app on the market. The sales for both apps were OK, but never really reached the levels we were hoping for. Since we did all the image-processing server-side, not a whole lot was left after all bills were paid. Enter iOS 5. When Apple introduced iOS 5, we realized that Apple just killed our project. iOS 5 came with a built-in red eye removal tool. We were bummed out about it, but didn’t know what to do. We stayed in the denial phase for some time hoping that sales wouldn’t suffer. That was of course naive. We talked about either twisting the idea into something more unique, or do something new entirely. Since all three of us were involved with other projects, and this wasn’t the primary company for any of us, we decided to shut down the company. But what about Red iGone? What should we do with it? We felt that the most reasonable thing to do at that point was to simply open source the entire project. So we did. As a result, you can now find the entire code base for everything from the web app, to the iOS apps and the back-end on [Github](https://github.com/teamdevify/Red-iGone) and it’s all licensed under GPLv3. And that’s the story of Devify. :) --- # Interviewed by TechCrunch URL: https://vpetersson.com/2012/02/07/interviewed-by-techcrunch/ Date: 2012-02-07 I’ve already posted about the fact that we’ve released a massive update to [YippieMove](http://www.yippiemove.com). Today the legendary Silicon Valley-blog TechCrunch covered this in form of an interview with me. You can read the full interview [here](http://techcrunch.com/2012/02/07/yippiemove-wants-to-become-the-twilio-of-email-migration/). --- # Introducing the brand new YippieMove URL: https://vpetersson.com/2012/01/31/introducing-the-brand-new-yippiemove/ Date: 2012-01-31 ![The new YippieMove front-page](https://vpetersson.com/wp-content/uploads/2012/01/frontpage_small-600x432.png "The new YippieMove front-page")\ A large part of last year spent working on a major update to YippieMove. The new YippieMove is a complete redesign, from the bottom-up. That means new logo, new user interface and an improved back-end. It has never been easier to move email data across multiple email accounts and services. We’re really proud of this release, and we think we’ve made a major improvement to what was already great product. Among one of the most exciting things with this release is the soon-to-be-release API, that will allow you to integrate YippieMove into more or less anything. For an overview of the new changes, please read [this](http://wireload.net/2012/01/yippiemove-the-future-of-email-migration/) blog-post. If you just want to see YippieMove with your own eyes, simply visit [YippieMove](http://www.yippiemove.com). --- # Notes on MongoDB, GridFS, sharding and deploying in the cloud URL: https://vpetersson.com/2012/01/29/notes-on-mongodb-gridfs-and-sharding-in-the-cloud/ Date: 2012-01-29 [We](http://wireload.net)‘ve been using MongoDB in production for about six months with [YippieMove](http://www.yippiemove.com). It’s been an interesting experience and we’ve learned a lot. Contrary to many MongoDB deployments, we primarily use it for storing files in GridFS. We switched over to MongoDB after searching for a good distributed file system for years. Prior to MongoDB we used a regular NFS share, sitting on top of a [HAST](/2010/09/27/setting-up-a-redundant-nas-with-hast-with-carp/)-device. That worked great, but it didn’t allow us to scale horizontally the way a distributed file system allows. Enter MongoDB. Just like most people playing around with MongoDB, we started out with a simple [Replica Set](http://www.mongodb.org/display/DOCS/Replica+Sets), but are now in the process of switching to a sharded setup. In the post, I will go over some of the things we’ve learned when using MongoDB. ## Running MongoDB in the Cloud One major thing to keep in mind when deploying MongoDB is that it matters a lot if you are deploying in a public cloud or on dedicated server. We deployed in a public cloud. While we did get good performance compared to many other cloud vendors, the performance were of course not at par with beefy dedicated servers (but came with other benefits instead). The biggest constrain with running in the cloud is limited I/O performance. We get about 60-80MB/s in writes, and 80-100MB/s in reads from our disks. That isn’t super fast for something like MongoDB, and we need to keep that in mind when we design the architecture. When we started out, we started with a replica set with three members (Primary + 2x Secondary). That worked well initially, and it felt good knowing that all data was backed up 3x. The problem however was that during heavy load, we saw that the secondary nodes fell behind. If the heavy load lasted long, the secondary nodes fell far behind such that they fell out of the opsize, and hence couldn’t catch up. To cope with this, we changed our strategy a bit and turned one of the secondaries into an arbiter. That offloaded the primary server significantly. If one were to run with dedicated servers on beefy hardware, I do not think this would be an issue at all. This was primarily due to the low I/O performance, and a cloud-specific issue. The setup we ended up then with was as follows: ![](https://vpetersson.com/upload/primary_secondary_arbiter.png "Primary, Secondary and Arbiter") ## Replica Set Setting up a Replica Set with MongoDB is very straight forward. It is recommended that you use three servers for a replica set. Two of which should be more or less dedicated to MongoDB, while the third is the arbiter, and can run on another server that isn’t necessarily dedicated to MongoDB. The arbiter isn’t resource intense, and is only used to keep track of the other two servers and vote for which one should be the primary. The process of setting up a replica set is simple and the instructions can be found [here](http://www.mongodb.org/display/DOCS/Replica+Set+Configuration). The only thing you might want to do differently from that guide is to instead of adding two nodes, add one node and one arbiter (using the command ‘rs.addArb(“node:port)’). We also found it handy to set priorities for the two replica nodes. We can do this by simply bumping the node we prefer to be the primary to ’2′ using the following commands: cfg = rs.conf() cfg.members\[0\].priority = 2 rs.reconfig(cfg) Please note that cfg.member\[N\] is the list item from the top (the first one being 0). It’s **not** the id. More information is available [here](http://www.mongodb.org/display/DOCS/Reconfiguring+when+Members+are+Up). In case you need to take down the primary server for maintenance, you should use the StepDown-command. This will gracefully force the primary server to step down. To do this, log into the server and issue the following command: use admin rs.stepDown() Once you’ve validated that the server is ‘secondary’ you can shut the server down without worrying about dataloss. There are two other commands that you should also be aware about. The first command is: rs.status() This command gives you information about your replica set and replication. The second dommand is: db.serverStatus() This will give you detailed information about the individual node. ## Let the sharding begin Sharding is a bit more tricky than setting up a simple replica set, but a lot easier than sharding a sequel database. While it is not necessary that each member of a shard is a replica set, it is highly recommended for redundancy purposes. Hence, the way you should be thinking about sharding in MongoDB as a way to consolidate multiple replica sets. Here’s an illustration of how one would expand the replica set we described above with a shard.\ ![](https://vpetersson.com/upload/shard.png "Sharded") Let’s now assume that you have two replica sets configured and ready to go. How do you turn them into a shard? The first thing you will need is to set up config-servers and mongos-nodes. The config-servers holds critical data about your shard(s). If that data gets lost, you’re basically toast. Hence, you want at least three of these servers. Mongos is the router that all your nodes will communicate against. The clients won’t be able to tell the difference if they are talking to a mongos or a regular replica set, which is nice. You will also need to restart mongod with a few new flags. To spin up a regular mongod-node (primary or secondary), use this command (assuming the replica set name is ‘repl0′: sudo -u mongodb mongod --shardsvr --replSet repl0 --dbpath /mongo/repl0 --fork --logpath /var/log/mongodb/repl0.log To spin up a config-server, use the following command (note oplogsize is set to 1 to minimize disk space being wasted): sudo -u mongodb mongod --configsvr --dbpath /mongo/configsvr --fork -–oplogSize 1 --logpath /var/log/mongodb/configsvr.log Finally, you need to spin up the mongos (we assume node1, node2, node 4 and node5 are the config-servers): sudo -u mongodb mongos --configdb :,:,:,: --fork --logpath /var/log/mongodb/mongos.log Once you have all the servers up and running, it’s time to start sharding. Start by opening a mongo-session against one of the mongos-servers. Now we need to add the replica sets to the the shard: use admin db.runCommand( { addshard : "repl0/:,:,:", maxSize:10000/\*MB\*/ } ); db.runCommand( { addshard : "repl1/:,:,:", maxSize:10000/\*MB\*/ } ); The should add both replica sets to the shard. We also specified that the maximum storage each replica set should hold to 10GB. Please note that this is a soft limit, and the balancer will use this as a guideline to evenly spread the data. Now we have prepared everything that needed preparation. Now it’s time to actually shard the data. Let’s assume that we have a collection/database named MyStuff that we want to shard. Let’s also assume that this is primarily used for GridFS. Once again, log into mongos, but now run the following commands: use admin db.runCommand( { enablesharding : "MyStuff" } ); Next we need to tell Mongo how to shard the data. Let’s use the files_id: use MyStuff db.fs.chunks.ensureIndex( { files_id: 1 } ); db.fs.files.ensureIndex( { files_id: 1 } ); db.runCommand( { shardcollection : "db.fs.chunks", key : { files_id : 1 } } ) db.runCommand( { shardcollection : "db.files.chunks", key : { files_id : 1 } } ) Depending on your load, using files_id can be a bad idea as won’t evenly distribute the load across the nodes. However, this is a whole different topic. Once you got everything setup, you might want to know more about how your system. There are a few commands that will give you a good overview about your system (in addition to the ones mentioned above). To get more information about your system, you might find the following commands useful: db.printShardingStatus() db.runCommand( { listShards : 1} ); Another useful command, if you need to reorganize your setup, is removeshard: db.runCommand( { removeshard : "repl0" } ); It’s likely that you want to learn more about MongoDB before you get started. I would then recommend the following resource: - [Sharding Introduction](http://www.mongodb.org/display/DOCS/Sharding+Introduction) - [Configuring Sharding](http://www.mongodb.org/display/DOCS/Configuring+Sharding) - [Choosing a Shard Key](http://www.mongodb.org/display/DOCS/Choosing+a+Shard+Key) - [How to Choose a Shard Key: The Card Game](http://www.snailinaturtleneck.com/blog/2011/01/04/how-to-choose-a-shard-key-the-card-game/) Please note that I’m by no means a MongoDB guru, but feel free to drop a comment below, and I’ll try to answer. --- # Countries and coffee consumption. URL: https://vpetersson.com/2012/01/28/countries-and-coffee-consumption/ Date: 2012-01-28 I’m a coffee junkie. Like many of my fellow geeks, I consume way more than the average person. On a normal day, I drink somewhere between 5-10 cups perhaps. How much is that in relation to the population at large? To find the answer, let’s turn to Wikipedia’s [List of countries by coffee consumption per capita](http://en.wikipedia.org/wiki/List_of_countries_by_coffee_consumption_per_capita). Let’s assume that all the data in there are true. Let’s also make the assumption that one serving of coffee is about 6 grams of coffee beans. In the graph below, I’ve included the top 6 countries, plus Italien and the United States (as they were interesting references). \[easychart type=“vertbar” height=“400” title=“Coffee Cups Consumed per Capita and Day” groupnames=“Finland, Norway, Iceland, Denmark, Netherlands, Sweden, Italy, United States” valuenames=“” group1values=“5.48” group2values=“4.52” group3values=“4.11” group4values=“3.97” group5values=“3.84” group6values=“3.74” group7values=“2.69” group8values=“1.92”\] So what can we make out of this? It is clear that the Scandinavian countries consume a lot of coffee. What’s interesting however is the low consumption per capita of the United States and Italy. As far as the United States go, I think the average resident consumes more than 1.92 cups of coffee per day, but the servings are weaker than 6 grams per serving. Hence, the per capita consumption of pure coffee beans remains low in comparison. Italy is also interesting. One would expect the home of espresso and cappuccino would have a higher consumption. Yet, that’s not the case. --- # Benchmarking and tuning FreeBSD's VirtIO network driver. URL: https://vpetersson.com/2012/01/24/benchmarking-and-tuning-freebsds-virtio-network-driver/ Date: 2012-01-24 In the [previous post](https://vpetersson.com/2012/01/23/benchmarking-virtual-network-drivers-under-freebsd-9/), I benchmarked three different virtual network drivers under FreeBSD. The clear winner was, perhaps not very surprisingly, the VirtIO network driver. In this article I will do some further benchmarking and try to optimize the driver further. Similarly to in the last post, I will use two FreeBSD 9.0 boxes with 2GB RAM and 2GHz CPU. Both nodes are set up with a private network and running in a public cloud (at CloudSigma). As many of you might know, running tests in a public cloud is difficult. For instance, you can’t control the load other nodes puts on the host resources and network architecture. To cope with this, I ran all tests five times with a 60 second sleep in between. This of course, isn’t perfect, but it is at least better than a single test. ## The tests For each test, I ran five different sub-tests, namely one wich each of the following TCP window sizes: - 64K - 128K - 512K - 1024K - 1536K The actual tests used Iperf, and ran the following command on the server: iperf -s -w $WINDOW and then the following Bash-script on the client: #!/usr/local/bin/bash WINDOW=$1 OUT=$2 x=1 while \[ $x -le 5 \] do echo "Starting test $x" iperf -i 1 -t 30 -w $WINDOW -c TheOtherNode >> $OUT sleep 60 x=$(( $x + 1 )) done The variable ‘$WINDOW’ is, as you might have already figured out, the TCP window size for the given test. This test gives me a total of 155 data-points for each test (31*5). The three tuning-variables I wanted to benchmark in this tests were: - kern.ipc.maxsockbuf - net.inet.tcp.sendbuf_max - net.inet.tcp.recvbuf_max The reason I chose these variables were simply because the article [Enabling High Performance Data Transfers](http://www.psc.edu/networking/projects/tcptune/#FreeBSD) recommended that one should start there. ### Test 1 In test 1, I wanted to benchmark a vanilla FreeBSD 9 installation with the VirtIO network driver. The default values for the tuning-variables were used. These were: kern.ipc.maxsockbuf=2097152 net.inet.tcp.sendbuf_max=2097152 net.inet.tcp.recvbuf_max=2097152 \[easychart type=“vertbar” height=“400” title=“Throughput in MBits/sec” groupnames=“Average, Median, Min, Max” valuenames=“64k,128k,512k,1024k,1536k” group1values=“209.98,220.12,227.74,237.30,212.92” group2values=“180.00,200.00,216.00,234.00,200.00” group3values=“145.00,161.00,132.00,105.00,139.00” group4values=“304.00,373.00,402.00,422.00,349.00”\] ### Test 2 In test 2, I increased the kern.ipc.maxsockbuf see what impact that would have on the performance. The new settings would then be: kern.ipc.maxsockbuf=4000000 net.inet.tcp.sendbuf_max=2097152 net.inet.tcp.recvbuf_max=2097152 The change was made to both servers. \[easychart type=“vertbar” height=“400” title=“Throughput in MBits/sec” groupnames=“Average, Median, Min, Max” valuenames=“64k,128k,512k,1024k,1536k” group1values=“217.74,164.54,163.05,163.08,145.51” group2values=“226.00,158.00,166.00,165.00,146.00” group3values=“134.00,106.00,93.30,0.20,59.80” group4values=“305.00,298.00,229.00,277.00,257.00”\] ### Test 3 The last test, I left kern.ipc.maxsockbuf set the above value, but I also increased net.inet.tcp.sendbuf\_max and net.inet.tcp.recvbuf\_max. The settings were then: kern.ipc.maxsockbuf=4000000 net.inet.tcp.sendbuf_max=16777216 net.inet.tcp.recvbuf_max=16777216 Similarly to in test 2, the change was applied to both servers. \[easychart type=“vertbar” height=“400” title=“Throughput in MBits/sec” groupnames=“Average, Median, Min, Max” valuenames=“64k,128k,512k,1024k,1536k” group1values=“128.51,151.89,141.17,157.74,152.62” group2values=“132.00,155.00,136.00,158.00,154.00” group3values=“79.70,94.40,70.30,69.20,81.80” group4values=“191.00,241.00,233.00,230.00,234.00”\] ## Comparing the results \[easychart type=“vertbar” height=“300” width=“350” title=“Average performance (MBits/sec)” groupnames=“Test 1, Test 2, Test 3” valuenames=“64k,128k,512k,1024k,1536k” group1values=“209.98,220.12,227.74,237.30,212.92” group2values=“217.74,164.54,163.05,163.08,145.51” group3values=“128.51,151.89,141.17,157.74,152.62”\] So what kind of conclusion can we draw from this? I’m inclined to say nothing. The numbers appears to hint at that the systems performs best out-of-the box. Maybe the values I tuned turned out to have very little impact in a virtual environment. Maybe the uncontrollable variables in a public cloud (eg. load from other nodes on the hardware and networking architecture) impacted the data and invalidated the data. Perhaps another explanation is that we’re pushing the capped limit upstream, and hence are unable to see any significant difference between the tests. Since I’m by no means a TCP/IP expert, I’d be curious to learn what other people with more experience think about this test, and where I should look to push performance further. --- # Benchmarking (virtual) network drivers under FreeBSD 9 URL: https://vpetersson.com/2012/01/23/benchmarking-virtual-network-drivers-under-freebsd-9/ Date: 2012-01-23 With the launch of FreeBSD 9, I was curious to learn how the VirtIO driver performed. I’ve seen a significant boost in disk performance, but how about the network driver? Luckily, that’s rather easy to find the answer to. I spun up two FreeBSD 9 nodes on [CloudSigma](http://cloudsigma.com/) and configured them with VirIO (just like in [this guide](https://vpetersson.com/2012/01/16/how-to-upgrade-freebsd-8-2-to-freebsd-9-0-with-virtio/)) and a private network. Once they were up and running, I installed [Iperf](http://sourceforge.net/projects/iperf/) and started testing away. I had three different network drivers that I wanted to benchmark: - Intel PRO/1000 (Intel 82540EM chipset) - RealTek RTL8139 - VirtIO (QEMU/KVM) One would of course assume that the VirtIO driver would outperform the others, but I wanted to see if that assumption was true, and if so, by how much. The FreeBSD virtual machines I used had 2GHz CPU, and 2GB of RAM. They also both used a VirtIO block-device as storage. The Iperf command I used on the server was: iperf -s and then on the client: iperf -i 1 -t 30 -c TheOtherNode So what were the findings? As you can see below, the VirtIO-driver performed better than all other drivers across the board. \[easychart type=“vertbar” height=“400” title=“Throughput in MBits/sec” groupnames=“VirtIO, Intel PRO/1000, RealTek RTL8139” valuenames=“Average, Median, Min, Max” group1values=“256.24,255.00,165.00,328.00” group2values=“209.03,209.00,197.00,215.00” group3values=“186.34,184.00,102.00,259.00”\] It should be said that the benchmarks I did only benchmarked traffic in one direction. Another thing that I didn’t capture in these tests were CPU usage. That would have been interesting to see, and I suspect that the VirtIO would require lest CPU power (at the very least on the host). While I performed these benchmarks on CloudSigma’s architecture, since they are running KVM/Qemu, they should be a good indicator of general performance under KVM/Qemu. If you found this interesting, you’ll probably also like the article [Benchmarking and tuning FreeBSD’s VirtIO network driver](https://vpetersson.com/2012/01/24/benchmarking-and-tuning-freebsds-virtio-network-driver/). --- # How to upgrade FreeBSD 8.2 to FreeBSD 9.0 with Virtio URL: https://vpetersson.com/2012/01/16/how-to-upgrade-freebsd-8-2-to-freebsd-9-0-with-virtio/ Date: 2012-01-16 Some time ago, I wrote about [how to use Virtio with FreeBSD 8.2](/2011/10/20/how-to-use-virtio-on-freebsd-8-2/). As I pointed out in the article, the performance was not nearly as good in FreeBSD 8.2 as it was in 9.0-RC1. Hence I wanted to get all my nodes over to 9.0 as soon as possible to take use of the massive boost in I/O performance. In this article I will walk you through the process of updating an existing system from FreeBSD 8.2 (without Virtio) to 9.0 with Virtio. If you’re just curious on how to get Virtio working on a fresh FreeBSD 9.0 installation, skip to [Step 2](#virtio). ## Step 1: The upgrade Let’s get right to it. Here’s the first step in the upgrade process: freebsd-update upgrade -r 9.0-RELEASE Once all files have been fetched, you will be asked a number of questions about merging config-files. They all seemed reasonable to me, so I just answered ‘y’ to all of them, but it might differ for you. Make sure you **read** the diff before accepting it. If you get the following error: The update metadata is correctly signed, but failed an integrity check. Cowardly refusing to proceed any further. Then simply patch your freebsd-update using the following command ([source](http://lists.freebsd.org/pipermail/freebsd-stable/2011-October/064321.html)): sed -i '' -e 's/=_/=%@_/' /usr/sbin/freebsd-update and then re-run the upgrade command again. If that went fine, it’s time to update the actual system. To do that, run: freebsd-update install Onde the update is done, reboot your system: shutdown -r now When it comes back up, make sure you run the install-again to install again to intall the userland updates: freebsd-update install Once you’ve run this, you’ll get the message: Completing this upgrade requires removing old shared object files. Please rebuild all installed 3rd party software (e.g., programs installed from the ports tree) and then run `/usr/sbin/freebsd-update install` again to finish installing updates. This is of course a massive pain in the butt, but you need to do this nonetheless. Depending on how many packages from ports you have installed, this may take everything from a few minutes to a **long** time. The easiest way to do this is to run portupgrade (if you don’t have portupgrade, install it from `sysutils/portupgrade`): rm /var/db/pkg/pkgdb.db && pkgdb -Ffuv && portupgrade -afp I added the ‘p’-flag, as this allows you to run ‘portupgrade -afP’ on other nodes (assuming you have a shared ports-tree) and just install the packages without having to re-compile them. Finally, when you’ve done this, you can run (for the last time): freebsd-update install ## Step 2: Installing Virtio {#virtio} Nowadays, Virtio is available in ports. That’s of course great, as that reduces the burdan of installing it. All you need to do is to run: cd /usr/ports/emulators/virtio-kmod && make clean install Once the kernel-module is installed, add the following to /boot/loader.conf: virtio_load="YES" virtio\_pci\_load="YES" virtio\_blk\_load="YES" if\_vtnet\_load="YES" virtio\_balloon\_load="YES" Next, we need to tell the system to actually use Virtio. The above commands assume that you are using ‘emX’ as your network-interface and /dev/daX or /dev/adX as your harddrive. It also that you’re using /etc/pf.conf as your firewall config, and that you have coded it to use the NIC’s name and not just IP-address. If you’re not using PF or use a different setup, simply skip the last command. cp /etc/fstab /etc/fstab.bak && cat /etc/fstab.bak | perl -pe "s/ad/vtbd/g; s/da/vtbd/g;" > /etc/fstab cp /etc/rc.conf /etc/rc.conf.bak && cat /etc/rc.conf.bak | perl -pe "s/em/vtnet/g;" > /etc/rc.conf cp /etc/pf.conf /etc/pf.conf.bak && cat /etc/pf.conf.bak | perl -pe "s/em/vtnet/g;" > /etc/pf.conf Now power off the system to make the changes to the host: shutdown -p now When the system stops, update all network drivers to Virtio and change the primary disk to block-driver. You should now be able to boot into the new system with Virtio and enjoy a lot better (and more reliable) speed. --- # Introducing KISS-surveillance -- The dead simple surveillance solution URL: https://vpetersson.com/2012/01/08/introducing-kiss-surveillance-the-dead-simple-surveillan/ Date: 2012-01-08 You’ve probably already noticed that I’ve used [ZoneMinder](http://www.zoneminder.com/) a bit. I published a few [blog-posts](https://vpetersson.com/tags/zoneminder/) on how to set up ZoneMinder, and even posted full [virtual appliance](https://vpetersson.com/open-source/zoneminder-virtual-appliance/) for ZoneMinder. The problem with ZoneMinder though, in my opinion, is that it is overkill for most users. Yes, it comes with some really cool features, but if all you want to do is to snap one image per second from an IP-camera for instance, it is way too complex. Don’t get me wrong, ZoneMinder is a great application if you have complicated surveillance needs. This just wasn’t my case. After running into some issues with ZoneMinder I decided to write a lightweight alternative, namely [KISS-surveillance](https://github.com/vpetersson/KISS-surveillance). It doesn’t even offer a fraction of all features ZoneMinder does, but it come with a few major benefits: - Uses a fraction of the resources of ZoneMinder. - Fast and easy to setup. - No database-requirement. Just plain files. - Only uses simple UNIX-tools like wget, crontab and Supervisor. - If you want to view the images from a web-browser, simply configure the web-server of your choice to do an index on the given folder (I prefer Nginx). ![](https://vpetersson.com/wp-content/uploads/2012/01/KISS-surveillance-600x493.png "KISS-surveillance") I’ve used this tool now for a few months now without any issues whatsoever. It might not be pretty, but it is simple and reliable, which for me is all I need when it comes to video surveillance. As far as compatibility goes, KISS-surveillance should work with any IP-based camera that supports static images over HTTP or HTTPS. For further instructions and to download KISS-surveillance, please [visit its home on Github](https://github.com/vpetersson/KISS-surveillance). --- # MongoDB and logrotate URL: https://vpetersson.com/2011/12/22/mongodb-and-logrotate/ Date: 2011-12-22 I’ve been using MongoDB now for some time in production. It’s great, and I really love how easy it is to set up and scale with replica sets etc. There is one thing that bugs me with MongoDB though. Instead of following the praxis of using Logrotate, they’ve decided to re-invent the wheel by building in a log rotation-feature (that is less powerful than Logrotate). The documentation on MongoDB’s log rotation-feature can be found [here](http://www.mongodb.org/display/DOCS/Logging), but the gist of it is that you send ‘_kill -SIGUSR1 PID_‘ to MongoDB and it will automatically rotate and rename the old logfile to something like ‘_mongod.log.2011-12-22T17-05-50_‘. There are some issues with this. If we were to only rely on the built-in log rotation-feature, we would need to address the issues of vacuuming old files (as this is not supported). Moreover, the built-in logrotation feature doesn’t support compression of old logs, which means your old log files will take up a lot more space. There is however a way to use MongoDB with logrotate. It’s just a bit more messy than most other applications. If we assume that MongoDB is configured to spit out log-files to ‘_/var/log/mongodb/_‘, we could simply use the following logrotate script: /var/log/mongodb/mongod.log { daily missingok rotate 7 compress delaycompress notifempty create 640 mongodb mongodb sharedscripts postrotate killall -SIGUSR1 mongod find /var/log/mongodb/ -type f -regex ".*\\.\\(log.\[0-9\].*-\[0-9\].*\\)" -exec rm {} \\; endscript } The real ‘hack’ here is the somewhat ugly find-command that deletes the log-file generated by MongoDB. It uses a Regular Expression that only matches the MongoDB-generated files, and leaves the logrotate-files alone (as they are named mongodb.log.\[0-9\].gz). --- # Puppet on Ubuntu 10.04 URL: https://vpetersson.com/2011/12/18/puppet-on-ubuntu-10-04/ Date: 2011-12-18 Yesterday I decided that it’s about time to learn [Puppet](http://puppetlabs.com/). I’ve had my eye on both Puppet and [Chef](http://wiki.opscode.com/display/chef/Home) for some time now. Yesterday after reading this [Quora](http://www.quora.com/What-are-the-key-reasons-to-choose-Puppet-over-Chef-or-vice-versa)-thread and this [blog](http://devopsanywhere.blogspot.com/2011/10/puppet-vs-chef-fight.html)-post, I decided to go with Puppet. After downloading their test-VM and going through the [tutorial](http://docs.puppetlabs.com/learning/), I pretty quickly fell in love with the simplicity and structure. Puppet is straight forward and rather intuitive. One of the architectures that I wanted to deploy Puppet on was running Ubuntu 10.04 LTS. Unfortunately, the version from Ubuntu’s repository is really old (0.25.4), and not really compatible with much of the cool things you can do with Puppet. Fortunately, PuppetLabs do provide their own repository, but the instructions for adding this repo wasn’t really at par with the rest of their excellent documentations — hence this post. If you’re a die-hard Ubuntu/Debian-fan, this is probably pretty straight-forward, but if you’re not, here is what you need to do: sudo su - echo -e "deb [http://apt.puppetlabs.com/ubuntu](http://apt.puppetlabs.com/ubuntu) lucid main\\ndeb-src [http://apt.puppetlabs.com/ubuntu](http://apt.puppetlabs.com/ubuntu) lucid main" >> /etc/apt/sources.list apt-key adv --keyserver keyserver.ubuntu.com --recv 4BD6EC30 apt-get update apt-get install puppet Ok, so that was pretty straight forward. The only tricky part was importing the keys, but now you know how to do that too. --- # I'm quoted in the December-issue of Computerworld URL: https://vpetersson.com/2011/12/15/im-quoted-in-the-december-issue-of-computerworld/ Date: 2011-12-15 ![](https://vpetersson.com/wp-content/uploads/2011/12/Computerworld.png "Computerworld")\ It’s always fun when magazines ask you for your advise. In the December-issue of Computerworld, I was interviewed by Bob Scheier in the cover story and talked about how to create a successful and scalable cloud architecture. --- # This really bugs me... URL: https://vpetersson.com/2011/12/14/this-really-bugs-me/ Date: 2011-12-14 If you’ve ever tried to install [ImageMagick](http://www.imagemagick.org/) on FreeBSD, you’ve probably run into this issue too. You have a head-less box in some datacenter, you don’t want to bloat the machine with X11. You try to install the no-X11-version of Image Magick: cd /usr/ports/graphics/ImageMagick-nox11 && make install The next thing you know, you the dependency ‘print/ghostscript9-nox11′ gets installed. Notice that this is the ‘no-x11′ version. Yet, look at the fifth option from the top:\ ![FreeBSD Ports-failure](https://vpetersson.com/wp-content/uploads/2011/12/Screen-Shot-2011-12-14-at-1.34.06-PM.png "FreeBSD Ports-failure") Isn’t it pretty obvious that I **don’t** want X11 if I install the ‘nox11′ port? Why is that even an option? --- # How to use Virtio on FreeBSD 8.2+ URL: https://vpetersson.com/2011/10/20/how-to-use-virtio-on-freebsd-8-2/ Date: 2011-10-20 In the past few years, virtualization has been the big topic everybody keeps talking about. There are good reasons for that, but one thing that really annoys me as a hardcore FreeBSD-fan is how poorly FreeBSD performs virtualized. For some time, the Linux-community have been using the [Virtio](http://wiki.libvirt.org/page/Virtio)-drivers to boost both I/O and network performance. Simply put, Virtio is a driver written to cut out any unnecessary emulation on the host and as a result both reduce load from the host and improve performance. Unfortunately the FreeBSD-community haven’t been able to utilize this, as there were no port for this. Luckily that just changed and here’s how you enable it. Just as a disclosure, I’ve only tried the I/O driver on [CloudSigma](http://www.cloudsigma.com), and it seems to be stable both on 8.2 and 9.0-RC1. According to other [this](http://kdl.nobugware.com/post/2011/10/14/freebsd-90-guest-virtio-support-in-KVM/) post, the network driver should work too though. It should however be said that the I/O performance on 8.2 is **significantly slower** than on 9.0-RC1. ## Grab the source code I spoke briefly about how to compile the kernel in [this](https://vpetersson.com/2010/09/27/setting-up-a-redundant-nas-with-hast-with-carp/) article, but you only need to fetch the source code. You don’t actually need to recompile the system like the article says. ## Grab the Virtio-patch In order to grab the patch, you need to first install Subversion. Assuming you got the ports installed, installing this is easy: cd /usr/ports/devel/subversion make install With that installed, let’s grab the actual driver/patch. cd /usr/src/sys/dev svn co [http://svn.freebsd.org/base/projects/virtio/sys/dev/virtio](http://svn.freebsd.org/base/projects/virtio/sys/dev/virtio) cd /usr/src/sys/modules svn co [http://svn.freebsd.org/base/projects/virtio/sys/modules/virtio](http://svn.freebsd.org/base/projects/virtio/sys/modules/virtio) Assuming that went well, let’s build and install the driver. cd /usr/src/sys/modules/virtio make && make install The final step is to activate the driver at boot. To do so, just add the following lines to /boot/loader.conf virtio_load="YES" virtio\_pci\_load="YES" virtio\_blk\_load="YES" if\_vtnet\_load="YES" virtio\_balloon\_load="YES" That’s it. If you already got a Virtio-device installed, you should be able to simply reboot your machine and it should pop up. If not, you need to shut down machine entirely, add this device, and then bring it back up. Once it comes back up, you can verify that it showed up by running dmesg | grep -i virtio ## Replacing your primary disk with Virtio Once you’ve verified that the driver actually works, you might want to replace your disks with Virtio. To do that, all you really need to do is to modify your fstab. Please note that this is risky and if something fail, you won’t be able to boot your server. Assuming are aware of the risks, here’s a command that replaces ad (IDE disks) with vtbd (Virtio-disk). If you’re using SCSI-emulation, replace ‘ad’ with ‘da’. cp /etc/fstab /etc/fstab.bak cat /etc/fstab.bak | perl -pe "s/ad/vtbd/g;" > /etc/fstab Verify the result by manually looking in /etc/fstab. If everything looks fine, you should be able to reboot your system. Once it comes back up, it should be using Virtio. **Credits:** [Keep Da Link](http://kdl.nobugware.com/post/2011/10/14/freebsd-90-guest-virtio-support-in-KVM/). **Update:** I wrote a new post titled [How to upgrade FreeBSD 8.2 to FreeBSD 9.0 with Virtio](https://vpetersson.com/2012/01/16/how-to-upgrade-freebsd-8-2-to-freebsd-9-0-with-virtio/). You should really take a look at that post instead, as the steps are much easier. --- # How to get 50% discount on Swisscom's hotspot (and possibly also others) URL: https://vpetersson.com/2011/09/25/how-to-get-50-discount-on-swisscoms-hotspot-and-possibly/ Date: 2011-09-25 Last weekend I was staying at a Holiday Inn in the UK. As most geeks, one of the first thing I did after checking in was to hop on the wireless connection to pull down my email. Like many hotels, Holiday Inn outsource the management of their wireless guest network. This particular hotel was using a provider named [Swisscom](http://en.swisscom.ch/r) (I’m not sure if this true for all Holiday Inn hotels). The wireless plans were as follows:\ **Business 24 hour – £15** - ‘unlimited’ data access, but 250MB per session (whatever that is defined by). - Prioritized bandwidth **Economy 24 hour – £12** - 500 MB data transfer - 256kbit connection I went for the ‘Business 24 hour’ plan without much hesitation. Later that night I spoke to my friend who was staying at the same hotel and also hopped online, but on his iPad. He said that he only paid £7 for 24 hour access. I thought that was odd, but interesting. Was it the case that Swisscom actually charged less for data on an iOS device than on a regular laptop? That turned out to be the exact case. The next day after my 24 hours expired, I hopped online to renew my connection. This time however I tricked Swisscom to believe my laptop was an iPhone (using the plugin [User Agent Switcher](https://addons.mozilla.org/en-US/firefox/addon/user-agent-switcher/) for Firefox). As I did this, I was redirected to a mobile version of their site with a different price. I now had a new plan called ‘SmartAccess 24 hour’ for £7. What made this particular interesting was that the plan actually had less restrictions than ‘Business 24 hour’. It offered everything that plan did, but without the 250MB per session limit — **for less than half the price**. I picked that plan and for the next 24 hours I was able to surf at a greatly discounted rate. Part of the reason why I decided to write this article was because I’m curious if that is the case with other providers too. If you stay at hotels often, it would be great if you could try this trick and see if it works on other providers too and post a comment below. **Update:** This article made [Hacker News](http://news.ycombinator.com/item?id=3659845) today (months after being posted), and an interesting discussion arose. The perhaps most interesting comment was from [donnal](http://news.ycombinator.com/item?id=3660094) where he confirmed a similar behavior on his Kindle Fire on a US Airways flight. --- # Facebook knows exactly how many visitors you got on your site URL: https://vpetersson.com/2011/09/03/facebook-will-soon-know-exactly-how-many-visitors-you-go/ Date: 2011-09-03 Is it possible that Facebook know more about how many visitors a given website has than Alexa or Hitwise? I’m not talking about people sharing a link on Facebook and then tracking outbound clicks. I’m talking about capturing all visitors. Both Alexa and Hitwise used to both rely on browser-addons to capture this data. Since only a small percentage of users will install this add-on, they will have really rough data (and skewed towards non-technical users). So how can Facebook then acquire more accurate data than these traditional companies? It’s pretty simple. You know that Like-button that is showing up all over the web these days? Turns out that Facebook is hosting all those images (and you [cannot host this yourself as that is a Term of Service breach](http://www.jwz.org/blog/2011/09/surprise-facebook-doesnt-like-privacy-countermeasures/)). Since Facebook is hosting all those images, they know exactly (or at least could know) how many visitors you have on your website for each page you got a Like-button. No estimates, but exact up-to-date metrics. If you’re logged into Facebook and visit a site with a Like-icon, they can also track that you have visited. The bottom line is that Facebook knows a lot more than you might think about the web as a whole. --- # Quick and dirty way of fixing NUT on pfSense 2.0RC URL: https://vpetersson.com/2011/08/27/quick-and-dirty-way-of-fixing-nut-on-pfsense-2-0rc/ Date: 2011-08-27 One of my favorite Open Source appliances is [pfSense](http://www.pfsense.org/). It can turn any old machine into a very powerful firewall/router in 10 minutes or less. Also, it comes with a very handy GUI and the fact that it is based on FreeBSD makes it even greater. While version 2.0 isn’t stable yet, the 2.0RC is more or less considered stable. It’s also a major update to the 1.2-branch. I’ve used 2.0 on two ‘production’ firewalls now, and I think it more reliable than 1.2. There is however one major problem that bugs me with 2.0 — The Nut-package ([Network UPS](http://www.networkupstools.org/)) is broken. While I probably should have created a proper fix for the problem instead of this quick-and-dirty fix, I can’t find the time to do so. Start by installing the package. As you’ll notice, even if you play around with the settings, you can’t get it to work. Instead SSH into your pfSense-box. Kill all NUT processes that pfSense may have started (“ps aux | grep -e nut -e ups” should help you find them). Now dive into the folder which holds NUTs settings, namely “/usr/local/etc/nut”. This folder holds a few config files for NUT. The most critical one that you’ll need to fix is ups.conf. By default, this file will look something like this: user=root \[UPS\] driver=genericups port=auto The part that really screws up NUT is ‘port=auto’. That’s why it won’t start. Also, depending on your configuration, your file will look different. I have an APC Smart-UPS connected with a serial cable, so mine looks like this: user=root \[UPS\] driver=apcsmart port=/dev/cuau1 sdtype=0 Once you’ve made those changes, you should now be able to start NUT with: /usr/local/etc/rc.d/nut.sh start Assuming everything went well, you can now see the status of your UPS in pfSense’s NUT monitor. That’s all great. The problem is, as soon as you restart your pfSense box, or touch anything in ‘NUT Settings,’ you’ll overwrite all changes. To deal with that, we have to do a dirty hack. The settings pfSense overwrites your configs with are stored in ‘/usr/local/pkg/nut.inc’. Hence, a more elegant hack would be to update this file with appropriate settings. I didn’t have time to do that, so I instead told pfSense to write the config files to a different path. To do so, open up ‘/usr/local/pkg/nut.inc’ and edit line 37 from define('NUT_DIR','/usr/local/etc/nut'); to define('NUT_DIR','/usr/local/etc/nut-fake'); Also, don’t’ forget to create ‘/usr/local/etc/nut-fake’. Now when you reboot or change anything in NUT Settings in the US, the changes will be written to /usr/local/etc/nut-fake and not /usr/local/etc/nut. Hence, the changes you make won’t affect NUT. **Update**: I just upgraded to 2.0-RELEASE (the 2.0 stable-version) and this problem still remains. **Update 2**: After upgrading to 2.0.1, I can confirm that the issue still remains. Yet, this is probably a package-specific issue and not related to the version of pfSense per se. --- # Fixing "su: unknown login: %%PG_USER%%" on FreeBSD URL: https://vpetersson.com/2011/08/15/fixing-su-unknown-login-pguser-on-freebsd/ Date: 2011-08-15 Today as I was installing PostgreSQL 9.04 on a new server I encountered the following error: \[root@host ~\]# /usr/local/etc/rc.d/postgresql initdb su: unknown login: %%PG_USER%% Something obviously went wrong when the port was being installed. Most likely it’s a bug in the build-instructions for the port. No stress though, as there is an easy fix. Just open up `_/usr/local/etc/rc.d/postgresql_` and modify the line postgresql\_user=${postgresql\_user:-"%%PG_USER%%"} with postgresql\_user=${postgresql\_user:-"pgsql"} (or I suppose you could add “postgresql_user=pgsql” to rc.conf). You should now be able to initiate PostgreSQL. --- # Did you know you can create encrypted partitions in OS X Lion? URL: https://vpetersson.com/2011/08/01/did-you-know-you-can-create-encrypted-partitions-in-os-x/ Date: 2011-08-01 The most exciting new feature in OS X Lion for all paranoid techies out there was the introduction of full-disk encryption (FileVault 2). The previous version of FileVault only enabled you to encrypt your home directory. That was a good start, but it forced you to log out once in a while to recover disk space. This was extra painful if you were using a small SSD-drive. Another issue with FileVault 1 was that it prevented you to effectively use Time Machine. In order to backup your home directory, you had to log out. First then would Time Machine back up your files. This was inconvenient to say the least. On the topic of Time Machine, OS X Lion also introduced a new feature when it comes to Time Machine. You can now encrypt your backups. That’s great, as if you were to use FileVault 2 and Time Machine without encryption, your backups would be entirely unencrypted. As I was setting up my encrypted Time Machine-drive, I took a closer look how this was done. As it turns out, Time Machine is using a new partition type in OS X Lion to accomplish this. What’s even better is that you can do this on any drive. All you need to do is to re-format the partition in Disk Utility, and select ‘Encrypted.’\ ![](https://vpetersson.com/wp-content/uploads/2011/08/encrypt.png "encrypt")\ Now you can easily encrypt all your external drives, while making it entirely seamless for day-to-day operations. If you chose to store the passphrase in your keychain, you won’t even notice that the disk is encrypted (other than perhaps less I/O throughput and more CPU usage). --- # How to upgrade from Mac OS X Lion GM to Final release URL: https://vpetersson.com/2011/07/20/how-to-upgrade-from-mac-os-x-lion-gm-to-final-release/ Date: 2011-07-20 If you ran one of the beta releases of Mac OS X Lion, you probably ran into a problem when trying to upgrade to the [final release](http://itunes.apple.com/us/app/os-x-lion/id444303913?mt=12) of OS X Lion. When I tried to upgrade one of my dev-machines, I was prompted with an error saying “A newer version of this app is already installed on this computer” Of course, you know that isn’t the case, as OS X Lion, **just** came out. After some digging, I found the solution. It turns out that all you need to do is to hold the option key (⌥) while pressing install. That will force the Lion to install. --- # Rebuilding a Linux software RAID array URL: https://vpetersson.com/2011/06/18/rebuilding-a-linux-software-raid-array/ Date: 2011-06-18 The process is pretty straight forward, and I’m writing this as a ‘Note-to-self’ for future references than anything else. If anyone else find it useful, that’s great. ### Identify the broken drive Start by identifying the device as the system know it (ie. /dev/sdX or /dev/hdX). The following commands should provide you with the information: cat /proc/mdstat mdadm --detail /dev/mdX cat /var/log/messages | grep -e "/dev/hd" -e "/dev/sd" Once you’ve identified the drive, you want to know something more about this drive, as /dev/sdX doesn’t really tell us how the drive looks like. In my case, I have three identical drives, so the following command didn’t help me much, but maybe it does for you. hdparm -i /dev/sdX That should give you both the model, brand and in some cases even the serial number. Hence this should be plenty to identify the drive physically. ### Replace the drive Not much to be said here. I assume you already know this, but you need a drive of equal size or larger. ### Partition the new drive If your system boot up in degraded mode, then just boot up your system. If not, boot it off of a Live CD (I used Ubuntu’s LiveCD in ‘Rescue mode’). Once you’ve made it to a console, the first thing we need to do is to partition the new hard drive. The easiest way to do this is to use sfdisk and use one of the existing disks as the template. sfdisk -d /dev/sdY | sfdisk /dev/sdX (where sdY is a working drive in the array, and sdX is your new drive) ### Rebuilding the array The final step is to add the new drive to the array. Doing this is surprisingly easy. Just type the following command: mdadm /dev/mdZ -a /dev/sdX1 (assuming you want to add the partition sdX1 to the RAID array mdZ) Of that went fine, the system will now automatically rebuild the array. You can monitor the status by running the following command: cat /proc/mdstat --- # Save time (and keystrokes) in the terminal URL: https://vpetersson.com/2011/06/09/save-time-and-keystrokes-in-the-terminal/ Date: 2011-06-09 Do you have a few long commands that you keep typing in the terminal? Things like ‘cd /some/long/path/that/takes/forever/to/type’ or ‘mycommand -with -lots -of -variables’? If so, here’s something you’ll enjoy. Just open up ~/.profile in your favorite editor, and add the following lines: alias foo="cd /some/long/path/that/takes/forever/to/type" alias bar="mycommand -with -lots -of -variables" Now you don’t need to type those long commands ever again. All you need to do is to typ ‘foo’ or ‘bar’. You can of course replace foo and bar with anything you want, as well as the command. --- # Sync your devices over the air for free URL: https://vpetersson.com/2011/05/25/sync-your-devices-over-the-air-for-free/ Date: 2011-05-25 I hate wires. I particular hate having to use wires to syncing devices. Luckily, there are ways to avoid them (in most cases). If you are having issues with this, the solution is not to buy MobileMe. Google App/Gmail is all you need. I just posted up an [article](http://blog.yippiemove.com/2011/05/25/how-to-keep-your-contacts-and-calendars-in-sync-across-devices-for-free/) over at the YippieMove Blog that will tell you how. After reading it, you should be able to sync your Mac, iOS device(s) and Android device(s) wirelessly — for free. --- # Updates to WireLoad URL: https://vpetersson.com/2011/05/16/updates-to-wireload/ Date: 2011-05-16 2011 has been a big year for WireLoad. We have launched two brand new products ([Blotter](http://www.blotterapp.com/) and [Quiet](http://wireload.net/products/quiet/)) in a completely new vertical, namely the desktop market for Mac OS X. It has been a very interesting experience, and we certainly did not foresee the success we’ve had. Just a few weeks ago, Blotter was named [‘App of the Week](http://wiki.twit.tv/wiki/MacBreak_Weekly_239)‘ in Leo Leporte’s show MacBreak Weekly. Blotter has also been on the Top-10 list for productivity apps in the Mac App Store, and we’ve been the #2 app in the entire Korean Mac App Store (and top 10 in the entire Japanese Mac App Store). With all of this success, we knew that we had to do two things that we’ve had in mind to do for some time: - Convert WireLoad from a California LLC to a Delaware C-Corp - Redesign our website for [wireload.net](http://wireload.net) - Create a logo for WireLoad Now we have done this, and we feel a whole lot better. That said, 2011 is far from over and we have plans of doing some pretty amazing new things the year, so stay tuned. Now I just need to take care of this website, as this design is both broken and getting kinda old. I leave you with a screenshot of the new [WireLoad.net](http://wireload.net) website. ![The new WireLoad.net](https://vpetersson.com/wp-content/uploads/2011/05/wireload_net-600x442.png "The new WireLoad.net") --- # How to travel safely with your computer URL: https://vpetersson.com/2011/05/13/how-to-travel-safely-with-your-computer/ Date: 2011-05-13 Regardless if we travel in business or on a vacation, we tend to bring our computer with us (at least I do). Common sense tells us to not check in our computer, but there are other things that we really ought to do. The first thing you want to do is to use disk encryption. The reason for this is simple: if you lose your laptop, nobody can access your files. This really isn’t as difficult as it may sound to set up. Mac OS X comes with two built-in types of disk encryption: - [Encrypted disk images](http://support.apple.com/kb/ht1578) - [FileVault](http://docs.info.apple.com/article.html?path=Mac/10.4/en/mh1906.html) (encrypted home directory). The encrypted disk image is great if you just want to protect a few files, but FileVault is better if you want to protect all your files. It should however be noted that FileVault comes with a pretty major drawback: it only allows you to do TimeMachine backups when you are logged out of your account. It should also be mentioned that [Lion](http://www.apple.com/macosx/lion/) will come with full-disk encryption, which makes dealing with encryption as an end-user both easier and more secure. If you’re not on a Mac, you can instead use [TrueCrypt](http://www.truecrypt.org/), which is a great Open Source encryption tool available for all platforms (including Mac OS X). The second thing you want to do is to install [Prey](http://preyproject.com/). This is a really awesome free service that allows you to track your computer if it gets stolen. In addition to tracking your stolen computer geographically, it also allows you to snap picture using the webcam, along with other cool features. Prey is available to most platforms, including Android. With this advice on how to travel safely, I hope you have a pleasant next trip. PS. Do not forget to keep proper backups of your data. TimeMachine makes this easy, but don’t rely on a single copy of your backups. Hard drives are cheap, but your data is hopefully worth a lot (at least to you). Ds. --- # How to get FreeBSD's CARP working on CloudSigma URL: https://vpetersson.com/2011/03/23/how-to-get-freebsds-carp-working-on-cloudsigma/ Date: 2011-03-23 For a few months now, we’ve been working on migrating our physical architecture for YippieMove over to CloudSigma. We got everything up and running swiftly, with the exception of one thing: CARP. As it turns out FreeBSD’s CARP implementation doesn’t really work very well in a virtual environment. (For those curious about the details, please see [this](http://lists.freebsd.org/pipermail/freebsd-net/2011-March/028332.html) mailing list post.) In order to get up and running with CARP on CloudSigma, you need to do the following: - Download the [FreeBSD kernel source](http://www.freebsd.org/doc/handbook/kernelconfig-building.html) - Download this [patch](http://www.shrew.net/static/patches/esx-carp.diff) ([mirror](https://vpetersson.com/upload/esx-carp.diff)) - Apply the patch (cd /usr/src/sys/netinet && patch -p0 - Recompile and install your kernel (make sure to include “device carp” in your kernel config) - Add “net.inet.carp.drop_echoed=1″ to /etc/sysctl.conf - Reboot into the new kernel That’s it. You should now be able to set up CARP as usual. For more information on how to configure CARP, please see my article [Setting up a redundant NAS with HAST and CARP](https://vpetersson.com/2010/09/27/setting-up-a-redundant-nas-with-hast-with-carp/). That article also includes detailed instructions on how to download FreeBSD’s kernel source and how to compile your kernel. As a technical side note, I got this working with FreeBSD 8.2 and the kernel source from the RELENG_8 branch. **Credits:** Matthew Grooms for the patch and Daniel Hartmeier for great analysis. --- # Quiet - A great productivity app URL: https://vpetersson.com/2011/03/14/quiet-a-great-productivity-app/ Date: 2011-03-14 Last week we launched our second application for Mac OS X. The application is called Quiet and is the best productivity tool I have ever used. The idea behind Quiet is simple. If you were to remove all distractions and only focus on one task at the time, you get more work done. This is exactly what Quiet does. It simply allows you to focus. Let’s say you’re writing an important email that requires 100% of your attention. You would then simply bring up Quiet, and pick ‘Focus on Mail’ (assuming you’re using Apple Mail). Quiet would then bring up your Mail window on a black background. But that’s not it. In addition, Quiet will also do the following: - Put you in away mode on IM (Adium, iChat and Skype) - Disable the new-mail notification sound from Mail.App - Disable Growl Once you exit the ‘focus-mode’, everything will revert to normal. Here’s an example of how Quiet looks like in action: ![Writing an important email in Quiet](https://vpetersson.com/wp-content/uploads/2011/03/Quiet_email-600x375.png "Writing an important email in Quiet") I found it’s amazing how much work you’ll get done once you’ve removed all distractions. Quiet 1.0 is [available in the Mac App Store today](http://itunes.apple.com/us/app/quiet/id421759530?mt=12). For more information about Quiet, please visit the [product page](http://wireload.net/products/quiet/). --- # A really ugly solution to get a static path to a 3G modem URL: https://vpetersson.com/2011/01/24/a-really-ugly-solution-to-get-a-static-path-to-a-3g-mode/ Date: 2011-01-24 This solution is so ugly that I felt that I had to post it =). I had a problem. Whenever I plugged in or rebooted, the a 3G modem into a Linux machine, it appeared on a different path (/dev/ttyUSBX). That creates some issues, as I’m using to connect to the internet, and Wvdial is using a hardcoded path to the modem. To fix this, I had to manually edit the file every time it changed. That’s very annoying. Now add in the fact that this is sitting on a remote machine that I have little physical access to, this is a real problem. My initial approach was to turn to udev and write a rule for the modem that creates an automatic symlink, such as /dev/modem. Unfortunately, when you add usb\_modeswitch into the mix, it breaks. For some reason, usb\_modeswitch simply wouldn’t detect the modem when doing this, and hence render it useless. Instead, I figured, if I write a Bash-script that automatically creates a symlink, that would take care of the issue. Of course, it is very ugly, but it does indeed work. Now I can simply run this script in Cron and that way know that I always have the correct path to the modem. So how did this script look you may ask. This is how: #!/bin/bash MODEM=$(cat /var/log/messages |grep "GSM modem (1-port) converter now attached to" | tail -n 3 | head -n 1 | perl -pe "s/.\*GSM modem \\(1-port\\) converter now attached to (ttyUSB.\*)$/\\1/g;") CURRENT=$(ls -l /dev/modem | awk '{print $10}' | perl -pe "s/\\/dev\\/(ttyUSB.*)$/\\1/g;") if \[ $CURRENT != $MODEM \]; then rm /dev/modem ln -s /dev/$MODEM /dev/modem fi I never said it was pretty, but it does indeed work. If you wonder what the ‘head’ and ‘tail’ part is all about, it is because the system creates three paths, but only the first one works. **Update**: Turns out it is a bad idea to run this in Cron, as the logs will rotate. Instead, launch it at boot in rc.local, but make sure you insert a ‘sleep 10′ or similar to allow the modem to settle. **Update 2**: Turns out there is a far more elegant solution to the problem. The system automatically generates a symlink for you. In my case, the modem is accessible via:\ `_/dev/serial/by-id/usb-HUA\_WEI\_Huawei_Mobile-if00-port0_`\ This means that you can hard-code that path instead of having to run a silly script to generate a symlink for you. --- # Get better music recommendations in Spotify with Lastify URL: https://vpetersson.com/2011/01/23/get-better-music-recommendations-in-spotify-with-lastify/ Date: 2011-01-23 [Spotify](http://www.spotify.com/) is probably one of the greatest apps I’ve used in recent years. It is the sole reason why I barely ever use iTunes anymore. Why would I bother downloading music when I can have access to a far larger music library at any given moment? There is however one big problem with Spotify: How do you find new music? Spotify does come with a “What’s New”-section that I assume is somewhat tailored to my listening habits. Yet, it’s far from perfect. It doesn’t give you any recommendations for other artists that fits your taste. [Last.FM](http://www.last.fm/home) is a service that does this very well. What’s even better is that Spotify comes with built-in ‘scrobbling’ support for Last.FM (ie. sending what you are listening to to Last.FM). What I used to do when I got bored of my current playlists was to go to Last.FM and turn their recommendations into a playlist in Spotify. It worked great, but it was a tedious process. Now, along comes [Lastify](http://lastify.nl/) and does this completely automatically. It’s awesome. For some time, this is an idea that has been growing in my head. We (as in [WireLoad](http://wireload.net)) were thinking about launching something similar to Lastify for the past few months, but never had the bandwidth to do so. Now we don’t need to. Lastify does a great job already, so why reinvent the wheel. What makes Lastify even greater is that it completely seamless (you do not need to do anything yourself) and the playlists are periodically being updated. So how do you get your Spotify to become more intelligent with Lastify? - Create a [Last.FM](http://www.last.fm/)-account. - Enable ‘Scrobbling’ to your Last.FM account in Spotify by going into the preferences. ![](https://vpetersson.com/wp-content/uploads/2011/01/Scrobbling-in-Spotify-600x421.png "Scrobbling in Spotify") - Run Spotify with the Scrobbling enabled for a few days so that it can collect some data on your music taste. You can verify that the music is being submitted by visiting Last.FM. - When you have enough data collected in Last.FM, head over to [Lastify](http://lastify.nl/) and enter your Last.FM username. Lastify will then generate a custom playlist for you based on your taste. When it first launches, it will only include one track, but don’t worry, give it some time and it will be populated. That’s it. You no longer need to listen to the same playlists over and over. The playlist will automatically update every other day to reflect your latest recommendations from Last.FM. If you’re curious on how the playlist will look like, my playlist looks like [this](http://open.spotify.com/user/q42/playlist/1PGgunHKQCwTsiUYLtKtpD). --- # Blotter is now available URL: https://vpetersson.com/2011/01/06/blotter-is-now-available/ Date: 2011-01-06 As of a few hours, [Blotter](http://www.blotterapp.com/) is [available](http://itunes.apple.com/us/app/blotter/id406580224?mt=12) in the App Store for Mac. We are super excited and this launch, and the App Store is really impressive. Check it out right away! ![](https://vpetersson.com/wp-content/uploads/2011/01/Blotter-600x475.png "Blotter") --- # Blotter is soon available URL: https://vpetersson.com/2010/12/17/blotter-is-soon-available/ Date: 2010-12-17 One of the biggest announcement Apple made in recent time was the announcement of the [App Store for Mac](http://www.apple.com/mac/app-store/). If you didn’t see the initial pitch, it is similar to iTunes App Store for iOS (iPad, iPhone, iPod Touch etc), but for native Mac OS X desktop applications. We (as in [WireLoad](http://wireload.net)) wanted to be apart of this from the start, and got busy thinking about applications that we would like to see ourselves. The result is [Blotter](http://www.blotterapp.com) — an application that sits on top of your desktop and displays your calendar. ![](https://vpetersson.com/wp-content/uploads/2010/12/blotter-calendar-on-leaf-full-600x375.jpg "blotter-calendar-on-leaf-full") _Blotter in action._ There are a few things that makes Blotter really awesome. - It does not consume much resources (barely any really). - It connects directly with iCal, so no configuration is required. - It sits on top of your desktop without being bothering you. It simply sits on top of your wall paper, but nothing happens if you click on it for instance. It just sits there. - It’s flexible. You can configure it in several different modes and positions on the desktop. When App Store for Mac launches on January 6, you know at least one application that you need to get. I’ve been using Blotter for a few weeks now, and it’s hard to live without it. No longer do I miss meetings and events because I forgot to check the calendar (and/or set an alarm). Check out some more screenshots and info over at the [official Blotter page](http://www.blotterapp.com/). --- # Red iGone for iPad is now available URL: https://vpetersson.com/2010/12/15/red-igone-for-ipad-is-now-available/ Date: 2010-12-15 I’m really excited about this. [Red iGone](http://www.redigone.com), the red-eye removal tool I’ve been working on for some time, is [now available on the iPad](http://blog.redigone.com/red-igone-for-ipad-now-available). This is the first iPad application I’ve been working on, and the end-result is pretty awesome. While the web-version works great, the iPad version is far more intuitive, given the intuitive touch experience.\ ![](https://vpetersson.com/wp-content/uploads/2010/12/group_step1-600x450.jpg "group_step1")\ [Direct iTunes link](http://itunes.apple.com/us/app/red-igone/id406248751). Let me know what you think. --- # Implementing ASSP with Postfix on FreeBSD URL: https://vpetersson.com/2010/12/14/implementing-assp-with-postfix-on-freebsd/ Date: 2010-12-14 In this article, I will walk you through the process of setting up [ASSP](http://assp.sourceforge.net/). If you’ve never heard of ASSP, it is a great anti-virus and spam-fighting proxy that sits in front of your SMTP server. Under the hood, ASSP includes a lot of intelligent spam logic, but you won’t really have to worry about it. All you really need to know is that it great at fighting spam and that it is easy to set up. In this article I assume that you already got Postfix in place, but you can use ASSP with any other SMTP server, as it simply sits as a proxy between the user and the SMTP server. In essence, what you need to do is to re-bind your SMTP server to another port (eg. 125) or IP (eg. 127.0.0.1) and have ASSP listen on the public interface and relay the messages to your SMTP server. Let’s get started. ### Install ASSP We start by installing ASSP. Since this guide is for FreeBSD, we will use the ports-system. However, if you’re on a different platform, you can simply install ASSP from source. > cd /usr/ports/mail/assp/\ > make install Enable ASSP and Clamav by adding the following to /etc/rc.conf > clamav\_clamd\_enable=”YES”\ > clamav\_freshclam\_enable=”YES”\ > assp_enable=”YES” Add your domains from Postfix to ASSP: > postconf | grep -e ^virtual\_alias\_domains.* -e ^mydestination.* | perl -pe “s/^(mydestination|virtual\_alias\_domains)\\s=\\s(.*)$/\\2/g; s/\\s/\\n/g;” > /usr/local/share/assp/files/localdomains.txt (This simply extract the settings from Postfix and insert them to ASSP) Now start the two services: > /usr/local/etc/rc.d/assp start\ > /usr/local/etc/rc.d/clamav-clamd start\ > /usr/local/etc/rc.d/ clamav-freshclam start ### Configure ASSP Now we need to make some changes to ASSP. By default, ASSP’s webserver listen on port 55555, so you need to point your browser to your server and this port. (eg. http://yourhostname:55555). The default username is root and the password is ‘nospam4me’ Go ahead and make the following changes: - Second SMTP Listen Port: 587 - All TestModes ON: True You need this set for about a week while you train ASSP. - Prepend Spam Subject: \[Spam\] - Accept All Mail: The IP or hostname of your server - Local Domains: file:files/localdomains.txt - Local Domains,IPs and Hostnames: The IP or hostname of your server - Use ClamAV: True - Modify ClamAV Module: False - Port or file socket for ClamAV: /var/run/clamav/clamd.sock - Web Admin Password: Your password Press ‘Apply changes’. ### Modify Postfix When you’re happy with your ASSP configuration, we need to modify your SMTP server to listen on a different port (in this case, port 125). If you are using Postfix, open /usr/local/etc/postfix/master.cf and change the line: > smtp inet n – n – – smtpd to > 125 inet n – n – – smtpd You also need to comment out the following line by simply adding a ‘#’ to the beginning of the line. > submission inet n – n – – smtpd Now we need to restart both Postfix and ASSP to apply the changes (the sequence here is important): > /usr/local/etc/rc.d/postfix restart\ > /usr/local/etc/rc.d/assp restart Verify that all the services are running with > sockstat -4l | grep -e 25 -e 125 -e 587 If not, take a look in the following log-files: - `/var/log/maillog` - `/var/log/assp/maillog.txt` ### Training ASSP Once you got ASSP up and running, you are likely to get a lot of valid emails marked as spam the first days. That’s normal. ASSP needs some training. If you run across a message that was marked as spam but was indeed a valid message, simply forward it to **assp-notspam@assp.local**. In a similar fashion, if you run across a spam message that ASSP failed to detect as spam, simply forward it to **assp-spam@assp.local**. After about a week or so of training (depending on your volume), you can go ahead and disable ‘All TestModes’ in the web interface. Good luck! --- # How to create SSH 'bookmarks' URL: https://vpetersson.com/2010/12/05/ssh-tips-how-to-create-ssh-bookmarks/ Date: 2010-12-05 If you’re like me, you spend a lot of time in the terminal window. It’s not rare that I SSH into 10+ different servers in a day. Having easy-to-remember FQDN’s makes it easier, but sometimes that’s not possible. Sometimes you only have an IP to a server, perhaps the server has a really long FQDN, or perhaps SSH is running on some arbitrary port. That makes your life harder. Luckily there’s an easy fix for it. Many people do not know that SSH comes with a bookmarkish feature out-of-the-box. All you need to do is to open up _~/.ssh/config_ (create it if it doesn’t exist) and add something like this: > host foobar\ >  hostname aaa.bbb.ccc.ddd\ >  port 2224 (Please note that the second and third lines are indented with a space.) Now you can SSH into ‘foobar’ by simply typing: > ssh foobar And voila, you no longer need to remember all arbitrary ports, IPs and hostnames. All you need to remember is your bookmark. For more information on what you can do with ~/.ssh/config, please see [this](http://www.openbsd.org/cgi-bin/man.cgi?query=ssh_config) page. Please note that this works on pretty much any platform except for Windows. But then again, if you’re this savvy, you probably know better than to run Windows in the first place =). --- # Great interview with Jason Calacanis URL: https://vpetersson.com/2010/12/01/great-interview-with-jason-calacanis/ Date: 2010-12-01 If you’re into startups and technology, you should really watch the following interview with Jason Calacanis from the Fowa-conference in London. I really like how Jason describes the way he and his team work. I completely agree with him. There is no such thing as ‘balance’ in a startup. You’re either in to do it all the way, or you might as well go home. - [Part 1](http://thinkvitamin.com/business/jason-calacanis-interview-at-fowa-london-2010-part-1/) - [Part 2](http://thinkvitamin.com/business/jason-calacanis-interview-at-fowa-london-2010-part-2/) --- # Introduction to scaling a website URL: https://vpetersson.com/2010/11/27/introduction-to-scaling-a-website/ Date: 2010-11-27 Scaling architecture and web sites is really something I find exciting. I’ve playing with scaling various software for many years, but it is just recent years I’ve come to use this in production (primarily through [YippieMove](http://www.yippiemove.com)). If you’re curious about scaling your website, I can really recommend the following video lecture from Harvard. It goes though concept that you might have run across in various blog-posts before. It is not super-technical, so even if you’re new to programming etc., you should still be able to understand the entire lecture. Watch it on [Academic Earth](http://academicearth.org/lectures/building-dynamic-websites-scalability/) --- # How to recover from random Photoshop (font) crashes URL: https://vpetersson.com/2010/11/26/how-to-recover-from-random-photoshop-font-crashes/ Date: 2010-11-26 Photoshop and Illustrator are somewhat of required apps today. You realize how much you need these apps when they decide to not play ball. This have happened to me a few times, and it often relate to fonts. Photoshop simply crashes when I bring up the font tool. Given that this is probably one of the most frequently used tools, this is a pretty big deal. The bug-report isn’t that helpful either. You’ll only get something like: > Exception Type: EXC\_BAD\_ACCESS (SIGSEGV)\ > Exception Codes: KERN\_INVALID\_ADDRESS at 0×0000000000000108\ > Crashed Thread: 0 Dispatch queue: com.apple.main-thread Yeah, that’s hardly enough to even figure out that it the underlaying reason may be a corrupt font. A quick Google query showed that I was hardly the only person having this issue. There are numerous posts on Adobe’s forum on this, but few useful answers. One of the most common suggestions is to [remove duplicate fonts in Font Book](http://docs.info.apple.com/article.html?path=FontBook/2.2/en/5285.html). While I found a few duplicates, that didn’t do the trick. After much searching, I found a page called [Troubleshooting fonts](http://kb2.adobe.com/cps/843/cpsid_84363.html) at Adobe’s website. Solution 3 on that page includes a very handy script named [Font Test Script](http://kb2.adobe.com/cps/843/cpsid_84363/attachments/FontTest.jsx). What the font does is to go trough all the fonts on your system within Photoshop and log the result. If it runs across a corrupt font and Photoshop crashes, it logs the result. Next time you fire up the script, it will display the broken font. (See the [README](http://kb2.adobe.com/cps/843/cpsid_84363/attachments/FontTest_readme%5B1%5D.pdf) for more details.) Now you can simply disable that font from Font Book and you should be good to go. This script really saved me from a lot of frustration and the script should really be bundled with Photoshop. Also, Adobe should really improve the crash management. On a side-note, I really hate the fact that Illustrator and Photoshop are **required** apps today. While they are great apps, I hate to admit that there are really no viable alternatives (don’t say Gimp and Inkscape, because they are far from at par). This, and because of their proprietary formats, is why Adobe can maintain their insane price-tag. I really hope that [Pixelmator](http://www.pixelmator.com/) will grow to become a viable alternative in the years to come. --- # Must-have applications for Mac OS X URL: https://vpetersson.com/2010/11/13/must-have-applications-for-mac-os-x/ Date: 2010-11-13 We all have our must-have apps. Whenever we re-install our system, these are the first apps we want to get in so that we can get back to business. From time to time we discover new apps that are really mind-blowing and we wonder how we survived without them. For me, 1Password and Visor are two apps like that. Since we all have different must-have apps, I thought I’d share a list of the Apps that I consider must-have apps for Mac OS X. - [1Password](http://agilewebsolutions.com/onepassword) A super-handy password-manager. For a long time I used Keychain for this, but after coming across 1Password, I wondered how I survived without it. It’s super neat, can be configured to synchronize with Dropbox and is available for iOS and Android too. Password management have never been this easy. - [Adium](http://adium.im/) A great IM client for more or less all instant network services (including, GTalk, ICQ/AIM and MSN). - [Homebrew](http://mxcl.github.com/homebrew/) A very handy tool for installing Unix/Linux-tools. I use it to install tools like Git, Joe, Nmap and Wget. - [iLife](http://www.apple.com/ilife/) Goes without saying. - [iWork](http://www.apple.com/iwork/) Like Microsoft Office, but without the bloat and created by people who have a sense of design. - [Little Snitch](http://www.obdev.at/products/littlesnitch/index.html) A great tool if you are as paranoid as I am. This tool acts as firewall for outgoing connections. As soon as an application tries to make a connection to the outside world, LIttle Snitch will pop up and ask you if you want to allow the connection or not. - [OmniGraffle](http://www.omnigroup.com/applications/omnigraffle/) The best best tool around for plotting out workflows and structures. I use it a lot for making workflows of networks and applications. - [Skype](http://www.skype.com/) The de facto standard VoIP tool today. Get the unlimited calling plan and a Skype-in number, and you have your phone with you wherever you go. - [Socialite](http://www.realmacsoftware.com/socialite/) Great social network aggregator. Allows you to follow and post updates to Twitter and Facebook. - [Spotify](http://spotify.com/) The reason why I never use iTunes anymore. Unfortunately it is not available in the U.S. yet. It is also available for iOS and Android. - [TextMate](http://macromates.com/) A very handy and flexible text-editor. It is a bit pricy, but it will probably increase your productivity if you write code. - [Things](http://culturedcode.com/things/) A GTD-style todo manager. - [Transmit](http://www.panic.com/transmit/) The tool that takes FTP/SFTP/S3 to a whole new level. Works like a charm. - [Visor](http://visor.binaryage.com/) A brilliant add-on to Terminal. Creates a drop-down terminal that is triggered by a hot-key. I love this tool. - [VMware Fusion](http://www.vmware.com/products/fusion/) Probably the best desktop-virtualization tool on the market. - [Xcode](http://developer.apple.com/tools/xcode/) Installs Apple’s development tools, and compile tools such as GCC. **Update**: I forgot to add [VideoLAN](http://www.videolan.org/) to the list. In case you’ve been living under a rock for the past few years, it’s great video-player. **Update 2**: [Airfoil](http://www.rogueamoeba.com/airfoil/) is another great app I forgot to add to the list. It enables you to stream music over the network to other computers or to an Airport Express. I use it both at home and at the office. **Update 3**: Our app [Blotter](http://www.blotterapp.com/) is obviously also a must-have application for OS X. --- # Why Android-phones will never be at par with the iPhone (but still win) URL: https://vpetersson.com/2010/11/06/why-android-phones-will-never-be-at-par-with-the-iphone/ Date: 2010-11-06 Yesterday I read an article titled “[Why Apple can’t beat Android](http://venturebeat.com/2010/11/05/why-apple-cant-beat-android/)” over at VentureBeat. It was an interesting read, and Mr. Grim argued in the article that Android is here to stay, and that Android will soon dominate the smartphone market. I do not disagree with Mr. Grim. It makes a whole lot of sense. In the opening of the article, Mr. Grim states that Windows is ‘Big, ugly, buggy, clunky, and everywhere.’ I think that’s spot on, and I also think this is where Android is heading. I got my hand on a Nexus One about six months ago. I’ve been using it as my primary phone since. However, I’ve never really liked Android. Surely, there are some really awesome features in Android 2.2 (Froyo), like a WiFi Hotspot-feature, Over-the-air contact/calendar-sync, a deep Google Voice integration pretty good Skype integration. Yet, I can enjoy all of those features because the Nexus One is pure Android. My network operator cannot restrict features on my device. If you for instance purchased an Android phone from a network operator (and managed, against all odds, to find one with Android 2.2), chances are the WiFi Hotspot-feature has been disabled. In this article I’m going to try to explain why Android will never be at par with the iPhone (or rather, iOS). There are two parts to this argument. The first one relates to Google, and the second relates to the network operators and hardware manufacturers. Let’s start with Google itself. If you’re a User Interface (UI) and User-Experience (UX) junkie like me, you’re not very fond of Google’s design-work. Look at any Google product, and you will find that, while it does the job very well (in most cases at least), you never look at a Google product and think “Wow, this is a really beautiful product.” Why is that? Most Google products use [Google Web Toolkit](http://code.google.com/webtoolkit/) (GWT) to render the user elements. From a technical stand, GWT is a pretty neat idea, but it’s clearly an engineer-driven product, not a designer-driven product. Now let’s tie this back to Android. Similar to any other Google products, you do not look at Android and think ‘Wow, this is really beautiful.’ You may look at Android and say, ‘This is cool from a technical stand and I can \[insert geeky task\]‘ (which is quite frankly why I gave Android a shot). If you use Android for some time, there are a lot of things that start to bug you (or at least me). For instance, the UI is very inconsistent (and often plain ugly). I can tell that Google tried to make Android pretty. Some parts of Android is pretty decent, but then once in a while you’ll find a window or setting that looks plain ugly (such as the Alarm clock). The bottom line is that Google is an engineering-driven company. No matter how hard they try, when you have engineers deciding over UI/UX, you simply cannot make a beautiful product. This is why iOS will always be ahead of Android. At Apple, the designers makes the final decisions about UI/UX, not the engineers. This is also why the first version of iOS (the very first iPhone) had a better UI/UX than the most recent version of Android. Now let’s move on to the second reason why Android will never be at par with iOS: The hardware vendors and the network operators. Let’s start with the hardware vendors. Since Android is freely available, anyone can (at least in theory) build their own Android phone. Just put together the required hardware, install Android, and you’re set. This leads to a problem for hardware vendors: How do they differentiate themselves? Many vendors, such as HTC and SonyEricsson, thought the answer to that question was to build their own version of Android. Or to be more precise, to customize the UI. Unfortunately, most of the hardware vendors tend to be even more engineer-driven than Google and even less talented than Google when it comes to UI and UX. As a result, you end up with a product that is worse than what they started with. All in the name of differentiation. It should probably be said that HTC did a few improvements with their Sense UI, but in the end, these forks are bad for the Android eco-system. Another result of these customizations is that it creates a significant lag between the main Android-branch and these custom versions of Android. For instance, just a few months ago SonyEricsson released a new Android-phone (X10 Mini). At the time of the release, Android 2.2 was the latest stable Android version. Yet, SonyEricsson’s phone came with Android 1.6, which was released on September 15, 2009 \[[wiki](http://en.wikipedia.org/wiki/Android_(operating_system)#Update_history)\], almost a year older than the phone. Yet this is nothing unique with SonyEricsson. If you look at Android phones today, very few phones actually run Android 2.2 (which was released on May 20, 2010 \[[wiki](http://en.wikipedia.org/wiki/Android_(operating_system)#Update_history)\]). The Nexus One is one of the few phones on the market who is running Android 2.2, and it has been out for more than six months. My guess is that in the porting-issue doesn’t tell the whole truth to why most hardware vendors are so far behind. I think another reason is that these companies are used to operating with a ‘release and forget’ kind of workflow. Once the phone is out, forget about it and move on to the next one. That used to work fine when they developed their own proprietary operating system, but that all changed now, and these companies haven’t really realized that they need to change their operations. Now let’s move on to the network operators. Historically (at least in the U.S.), they’ve been able to have absolute control over all devices that they sell. They’ve pre-loaded them with crapware (both operator-specific and from software vendors who paid for having their apps installed). These crapware was a part of the phone, and the user wasn’t able to uninstall them. They were also able to explicitly set what your phone could and could not do (regardless of its actual capacity). When Android came along, the network operators realized that they could do the same thing with Android. It is a very lucrative business, so why wouldn’t they? The hardware vendors, who are used to this, simply accepted this and customized their own forks even further. This led to two things: even more latency between the main-branch of Android and the custom-fork, but perhaps more importantly, a far worse user experience. Some network operators took things even further and restricted Marketplace (which is used to obtain 3rd party apps). So how is this different from iOS? First, Apple control both the hardware and the software. While we cannot determine exactly how much AT&T have held back Apple from features to iOS, they have at least not bended over for AT&T and allowed them to pre-load the iPhone with AT&T-specific apps. Second, Since Apple also control both the operating system and the hardware, there is not latency in the releases. The day Apple release a new update to iOS, you can upgrade your device. You do not have to wait six months or more for it to be ported to your particular fork of iOS. With all of this in mind, it doesn’t change the fact that Android will become the largest operating system for smartphones over the next few years. There are far too many users out there who do not understand the difference (nor do they care). They only want a smartphone and they’ll ask the sales rep at their local \[insert favorite network operator\] for advice. The sales rep will obviously not tell them all of this. They will only tell them to buy their own phones because X, Y and Z (while in reality, it is the phone they make most commission from). Also, it’s hardly the fist time in history where an inferior product manages to capture the largest market share. --- # How to turn in your Mac for repair without downtime. URL: https://vpetersson.com/2010/11/02/how-to-turn-in-your-mac-for-repair-without-downtime/ Date: 2010-11-02 Let’s face it — all computers fail at one time or another. In my experience Apple computers break far less than PCs, and when they fail, Apple provide fast and great repairs. If you live in a country with Apple Stores, it’s even easier. When a PC breaks, you’re usually out of luck. Long hours on hold with some Indian call center awaits you and if you are able to convince them that your broken computer should be covered by the warranty, you are likely to spend weeks, if not months, without your computer. This article assumes that you do own a Mac, and that you do need to hand it over to Apple for a repair. This can take anywhere from a few days to a week or two, depending on what’s wrong with it. If you’re like me, the mere thought of being without your computer for more than a few hours is enough to case nightmares and, perhaps more importantly, a significant loss of productivity. Last week I was faced with this problem. After speaking with Apple on the phone, they said the repair would probably take at least a week, as I haven’t turned in my laptop for repair since I bought it, and just kept a list of things that needed to be repaired. After spending some time thinking how I could resolve this, I came up with a surprisingly effective solution that gave me literally no downtime. This is what you need: - A spare Mac Doesn’t matter if it is a desktop or laptop, as long as it is the same architecture (Intel/PowerPC). I used a Mac Mini. - An empty USB disk of the size, or greater than, the drive in your computer Now do the following: - Shutdown your computer - Boot it on your Mac OS installation disk - Launch Disk Utility within the installer - Format the USB disk with one ‘Mac OS Extended (Journaled)’-partition of the same size as your internal drive. - Make sure that the partition scheme is set to ‘GUID’ - Go to the ‘Restore’-tab - Drag your regular boot-partition into the ‘Source’-field - Drag the newly created partition into the ‘Destination’-field - Press ‘Restore’ This will clone the entire disk of your computer to the USB-drive. This process will probably take 10h+, so it is good thing to do before you leave the office for the night. Once the process is complete shut down the computer. Turn off your spare Mac and plug in the USB-drive. Turn on the spare Mac and hold down the ‘Alt’/’Option’-key. This will bring up a boot menu. You should now see the partition you created on your USB-drive. Select it. Once OS X boots up, you will notice that you have your entire system running. All apps and settings are there. You can just continue your work. You can now boot up your the Mac you are going turn in for repair on the Mac OS installation, wipe the disks, and perform a clean installation. Voila! You now have a clean Mac that you can turn in for repair without having to worry about your data being read by some technician, while you can continue your work on the spare Mac. When you get your computer back from repair, you can just repeat the process above in reverse, and you will be up and running on your old computer again. --- # Is Carbon Copy Cloner better than Time Machine? URL: https://vpetersson.com/2010/10/27/is-carbon-copy-cloner-better-than-time-machine/ Date: 2010-10-27 When Apple announced Time Machine, I was overwhelmed and thought it was the best invention since sliced bread. I’ve been using it since then in setups both with a dedicated external hard drive and a Network Attached Storage (NAS). Lately though, I’ve started to get more and more annoyed with Time Machine. It consumes a significant amount of resources, as it keeps backing up changes continuously, and it will fill up the destination drive until it’s full. Another great thing with Time Machine is the ability to simply recover a backup within the OS X installer. Simply boot up on the installation disk and pick the Time Machine drive, and off you go. That’s pretty awesome. Unfortunately, the recovery takes many, many hours. I’m fine with the fact that it takes sme time to copy my 200GBish backup over a gigabit network, right now my I’m staring at a screen saying the estimated time remaining as 26 hours. That’s just bizarre. No, the NAS is not that slow, I can easily copy files from the NAS in ~20MB/sec. Yes, I know there are plenty of small files in the backup, but that doesn’t explain this slow speed. I’ve done recoveries in the past from external drives, and I was equally surprised back then that the backup took that long. As I was sitting there staring at the screen, I pulled up my iPad trying looking for alternative solutions, I found [Carbon Copy Cloner](http://www.bombich.com/). It’s a free software from Bombich Software, and enables you to clone two drives within OS X (even the one that you booted from). It even allows you to schedule syncs. Since the software literally clones the drive, you can simply boot off of the backup drive (assuming it is formatted with GUID and HFS+). Upon finding this out, I started thinking: perhaps Carbon Copy Cloner is better than Time Machine. Simply hook up an external drive to your machine, create a partition of the same size as your local drive, and schedule it to sync every night. If your primary disk fails, you can just boot off of the cloned drive and literally have zero downtime. You don’t have to waste 12-24h recovering a Time Machine backup. There are obviously some drawbacks. First, it doesn’t back up continuously. It only back ups as often as you schedule it to do. Second, you cannot recover files in the same fashion as you can with Time Machine, i.e. you cannot recover a file in a given folder from 4 months ago. Yet, you will not waste I/O bandwidth during your workday on running backups. I’m not sure if it is better than Time Machine, and quite frankly, it probably depends on your usage pattern. I cannot recall the last time I entered Time Machine to recover a file from months ago, but I can recall plenty of time where I’ve been annoyed with Time Machine for consuming my I/O bandwidth when I needed it myself. Hence Carbon Copy Cloner might be a better fit for me. --- # How to merge PDF-files on Mac OS X URL: https://vpetersson.com/2010/10/27/how-to-merge-pdf-files-on-mac-os-x/ Date: 2010-10-27 In my [previous post](/2010/10/22/how-to-split-a-pdf-files-on-mac-and-linuxunix/), I talked about how to split PDF files on Mac OS X, Linux and Unix. In that article, I mentioned a simple app that allows you to merge several PDF files. The app is dead simple, and you can create it for yourself if you’d like (here’s the [workflow](https://vpetersson.com/wp-content/uploads/MergePDF_workflow.png) in Automator). All it asks you for is the input files (the files you want to merge), and it will take care of the rest. When done, Preview will pop up with the merged file. You will then have to save the file. You can download the application from [here](https://vpetersson.com/wp-content/uploads/MergePDF.zip). It works on Snow Leopard, and I’m not sure if it will work on earlier versions of Mac OS X. --- # How to manage VMware Server on Mac OS X with VNC URL: https://vpetersson.com/2010/10/24/how-to-manage-vmware-server-on-mac-os-x-with-vnc/ Date: 2010-10-24 VMware Server is a great product. It’s free and works well with most guest operating systems. However, there is **one major drawback** – you cannot use the ‘console’ app on Mac OS X. For some strange reason, VMware decided to only make the required Firefox plug-in available for Linux and Windows. Given that Mac OS X is the OS of choice for most tech-savvy users I know, this decision makes no sense at all. While I rarely need the console for an existing virtual machine (other than if it fails to boot or something similar), it is obviously required to install the operating system onto the virtual machine. Until recently, I had to either remotely log into a Windows or Linux machine (or even more ironically, open it in a local virtual machine). Luckily there is a workaround: the built in VNC-support. It is a bit annoying to have to do this, but it’s probably faster than having to log into a remote machine just to access the console.\ ![](https://vpetersson.com/wp-content/uploads/2010/10/VNC-on-Mac-OS-600x478.png "VNC on Mac OS") _VNC connection directly to a VMware virtual machine._ Here’s how you do it: - Create a new virtual machine in VMware Server. - Before booting up the virtual machine, open the .vmx-file that resides inside the folder for the new virtual machine. - Add the following line: RemoteDisplay.vnc.enabled = TRUE RemoteDisplay.vnc.password = mypassword RemoteDisplay.vnc.port = 5900 - Boot the virtual machine. This will start a VNC server on port 5900. You can use a regular desktop client to connect to it. You can even use the built-in VNC client on Mac OS X (“Go” -> “Connect to Server” -> “vnc://\[IP to your server\]:0″). If you’re not familiar with VNC, “:0″ is the first server and is the same as “:5900″. If you have multiple virtual machines you want to access, you would configure the next virtual machine to bind on port 5901 (:1 in VNC), and so on. ### Accessing the server remotely If you’re like me, you don’t have your VMware Servers on the same physical network as you are working from. In order to access them via VNC you need to do some magic with SSH. It’s really quite easy though. All you need to do is to create an SSH tunnel from your localhost and forward it to the appropriate port on the remote server. To do this, open up the terminal and run the following command: ssh -L 127.0.0.1:5900:192.168.10.2:5900 -N -vv username@remoteserver.com In this case we create a tunnel on localhost’s port 5900 and point it towards a remote server. The remote VMware server listens on the IP address 192.168.10.2, and the we’re connecting to port 5900. With the tunnel established, we can connect to the remote virtual machine with the built-in VNC client with the following steps:\ “Go” -> “Connect to Server” -> “vnc://127.0.0.1:0″). For more info on VMware and VNC, please see [this VMware KB](http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1246) entry. --- # How to split a PDF files on Mac, Linux or Unix URL: https://vpetersson.com/2010/10/22/how-to-split-a-pdf-files-on-mac-and-linuxunix/ Date: 2010-10-22 There are a ton of tools out there for modifying PDF-files. Most of them are crappy, overpriced sharewares from mediocre developers looking to make cash from non-technical users. What most Mac-users perhaps do not know is that Mac OS comes with a ton of handy tools for modifying PDF files. For instance, with a few clicks, you can create an app that merges different PDF files into one file with Automator (I will post that app in a separate post). One thing that the Mac does not do well though is to split a PDF file. Sure, you can “Print to PDF” and simply select the pages you wish to print. This works great if you’re only looking to split one file. If you have, let’s say 100 documents you wish to split, it’s not a very desirable approach. That’s where the handy toolkit [Ghostscript](http://pages.cs.wisc.edu/~ghost/) comes into play. Most Unix/Linux users have probably encountered it at one point or another (or at least seen the package being installed), as it is widely used in the the Unix/Linux world under the hood. Since Mac OS X is really originates from FreeBSD, we can easily tap into the wealth of Unix/Linux tools. I’m not going to get into the details on how to install Ghostscript on Mac OS X, as there are many ways of doing this (I personally prefer using [Homebrew](http://github.com/mxcl/homebrew), but you can as well use [Fink](http://www.finkproject.org/) or [MacPort](http://www.macports.org/)). If you’re on Unix or Linux, chances are you already have Ghostscript installed. Once you have Ghostscript installed, you can use this simple script. #!/bin/bash \# Usage ./pdfsplitr.sh inputfile.pdf outputfile.pdf pagenumber \# Example: ./pdfsplitr.sh myfile.pdf myotherfile.pdf 2 GS=$(which gs) \# Make sure Ghostscript is installed if \[\[ $GS = “” \]\] then echo “Ghostscript is not installed” exit fi \# Run the actual conversion. $GS -sDEVICE=pdfwrite -q -dNOPAUSE -dBATCH -sOutputFile=$2 -dFirstPage=$3 -dLastPage=$3 $1 The usage is really simple, if you save the script to ‘pdfsplitr.sh’, all you need to do (in addition to making it executable with ‘chmod +x pdfsplitr.sh’) is to provide it with the file you wish to split, the output-file and the page number, like this: ./pdfsplitr.sh myoriginal.pdf mysplittedfile.pdf 2 Enjoy! --- # Moving Cyrus from a 32Bit to a 64Bit server URL: https://vpetersson.com/2010/10/03/moving-cyrus-from-a-32bit-to-a-64bit-server/ Date: 2010-10-03 I’ve you’ve read the past few articles I’ve published, you’ve probably figured out two things: - I love FreeBSD. - I’m in the process of moving a bunch of servers. This time I’ll walk you trough how to move [Cyrus-IMAP](http://www.cyrusimap.org/) between a 32bit server to a 64bit server. In my case, on FreeBSD. Unfortunately the process is not as straight-forward as I imagined it to be. With these instructions, you will hopefully save yourself the hours I spend troubleshooting the issue. My first approach was to simply copy all the data from the old server to the new one. That didn’t quite do the trick. Cyrus wouldn’t launch properly, as it could not read the 32bit data properly. After some research I found out that the problem is Cyrus’ internal database is locked for the given architecture. Therefor you need to rebuild the data on the 64bit server. Here’s the full approach I took, step-by-step. I will assume that you have your configdirectory set to /var/imap and partition-default to /usr/imap. If not, you’ll have to adjust that below. I also assume that you’ve copied your Cyrus config files (imapd.conf and cyrus.conf) across ahead of time. ### Initial sync We start by sync the data between the two servers without shutting down Cyrus. On the old server, run: rsync -aP /var/imap root@mynewserver:/var/ rsync -aP /usr/imap root@mynewserver:/usr/ This will copy all data across without you having to shut down Cyrus (ie no downtime). If you have a large mail storage, this can take some time. Please note that you need to run the command above as ‘root’ (or some other user with full read-permission to the data). ### Final sync With the initial sync done, we have most of the data copied. We only need to do a final sync with Cyrus shut down. Hence, the first step is to shut down Cyrus on both sides. With Cyrus **shut down** on both servers, run the following on the source server: rsync -aP –delete /var/imap root@mynewserver:/var/ rsync -aP –delete /usr/imap root@mynewserver:/usr/ The final step on the source server is to extract the mailbox list. To do that, run the following: sudo -u cyrus /usr/local/cyrus/bin/ctl_mboxlist -d > ~/mboxlist.txt scp ~/mboxlist.txt root@mynewserver:/root/ ### Final tweaks on the new server This is the part that took me time to find. Namely the database issues that occur when switching from 32bit to 64bit. Here are the commands that will recreate the various databases. On the new server, run: sudo -u cyrus rm /var/imap/db/* /var/imap/db.backup1/* /var/imap/db.backup2/* /var/imap/deliver.db /var/imap/tls_sessions.db /var/imap/mailboxes.db sudo -u cyrus /usr/local/cyrus/bin/ctl\_mboxlist -u sudo -u cyrus /usr/local/cyrus/bin/ctl\_cyrusdb -r sudo -u cyrus /usr/local/cyrus/bin/tls_prune sudo -u cyrus /usr/local/cyrus/bin/ctl_cyrusdb -c sudo -u cyrus /usr/local/cyrus/bin/cyr_expire -E 3 When you’ve executed all of those commands, you should be able to launch Cyrus successfully. Also, if you’re not on FreeBSD, you might need to change the path to the binaries. Good luck! **Credits**: [Bill. C](http://www.mail-archive.com/info-cyrus@lists.andrew.cmu.edu/msg38092.html) on the Cyrus mailinglist. --- # Copying users/groups between two FreeBSD servers URL: https://vpetersson.com/2010/09/30/copying-usersgroups-between-freebsd-servers/ Date: 2010-09-30 Sometimes you want to move **all** users and groups from one server to another without having to recreate all users. Let’s say you are retiring an old server and moving to a new server. If you’ve had the server for a while and have lots of users on it, the last thing you want to do is to recreate all users and assign new passwords. If you’re on FreeBSD, the task is pretty trivial. I’m sure it’s pretty straight forward on Linux too, but these instructions only apply to FreeBSD. In my case I was moving the users from a FreeBSD 7.1 system to a brand new FreeBSD 8.1 server. There are three files you need to copy from the old to the new server. These are: - /etc/passwd - /etc/group - /etc/master.passwd You might also want to copy the entire /home directory, so that the users get their user data across. Once you’ve copied the files to the new system, you also need to rebuild the password database. To do that, run: pwd_mkdb -p /etc/master.passwd The easiest way to get all that data across is to copy them over SSH with scp. You will need to enable root-login in sshd, but other than that, it is very straight forward. Here’s an example of all of the above if you’re logged into the new server. scp -r root@myoldserver:/usr/home/* /usr/home/ scp root@myoldserver:/etc/passwd /etc/ scp root@myoldserver:/etc/group /etc/ scp root@myoldserver:/etc/master.passwd /etc/ pwd_mkdb -p /etc/master.passwd Pretty simple, huh? --- # Annoying SEO scam URL: https://vpetersson.com/2010/09/27/annoying-seo-scam/ Date: 2010-09-27 So I’m not 100% confident that this is a scam, but it sure looks like it. I’ve been contacted by this guy a few times by now. Here’s the email he sent out. Also, I’m pretty sure there is no such thing as the “reverse google pagerank algorithm”. > Hi, > > John Stahl here. I just wanted to drop you a line and invite you\ > to be a link partner for our website xraider.com. > > I’ve found your website with the “reverse google pagerank\ > algorithm” which indicates that we both would get better google\ > rankings, when we exchange links. > > I’ve already gone ahead and added your site viktorpetersson.com\ > to our link directory, could you please verify the description\ > before it will go life at: > > http://www.xraider.com/links/show.php?aHR0cDovL3d3dy52aWt0b3JwZXRlcnNzb24uY29tLw== > > Talk soon,\ > John Stahl Just thought I’d share it with all of you have in case you have received a similar e-mail. I’m pretty sure neither Matt Cutts nor Rand Fishkin would approve of using the “reverse google pagerank algorithm” even if it did exist. When it comes to SEO, I’m rather safe than sorry and use standard white-hat practices. **Update**: John Stahl sent me another message today. The exact same message for the fifth time. --- # Setting up a redundant NAS with HAST and CARP URL: https://vpetersson.com/2010/09/27/setting-up-a-redundant-nas-with-hast-with-carp/ Date: 2010-09-27 One of the coolest new features in FreeBSD is [HAST](http://wiki.freebsd.org/HAST) (Highly Available Storage). In simple terms, it can be described as RAID1 (mirror) over TCP/IP (similar to DRBD on Linux, but native). You can simply have two physical nodes replicate data over the network. If you throw in [CARP](http://www.freebsd.org/doc/handbook/carp.html) (Common Address Redundancy Protocol) to the mix, you can create a very robust storage system on commodity hardware with automatic failover. After reading trough the official HAST wiki, I decided that the approach outlined there wasn’t the ideal approach for us. They use UCARP, which is a userland implementation of CARP. I prefer to use the real deal, as it is known to be extremely robust. The only downside with using CARP instead of UCARP is that it requires that you recompile the kernel. In order to get this working, you need two FreeBSD 8.1 machines connected with gigabit ethernet (at least gigabit is preferred) and one spare partition or disk on each machine (preferably same size). Here are the steps: - Recompile the kernel with CARP-support on both nodes - Set up CARP - Configure and set up HAST - Configure the failover ### Recompile the kernel I’m going to run trough this process real quick. You can find more details in the official documentation [here](http://www.freebsd.org/doc/handbook/kernelconfig-building.html). **Install CSVup** cd /usr/ports/net/cvsup-without-gui make install Edit the CSVup config-file (/usr/share/examples/cvsup/stable-supfile) - Add a mirror close to the servers from [here](http://www.freebsd.org/doc/en/books/handbook/cvsup.html). - Change “*default release=cvs tag=” to your version of FreeBSD (eg. RELENG\_8\_1). Update/retrieve the source: cvsup -L 2 /usr/share/examples/cvsup/stable-supfile Configure the kernel. In this case we use an AMD64 CPU, but you can replace that with i386 if that’s what you have: cd /usr/src/sys/amd64/conf mkdir /root/kernels/ cp GENERIC /root/kernels/MYKERNEL ln -s /root/kernels/MYKERNEL joe MYKERNEL To enable CARP, add the following \# CARP device carp When done configuring, build and install the kernel. cd /usr/src make buildkernel KERNCONF=MYKERNEL make installkernel KERNCONF=MYKERNEL If everything went fine, reboot your system into the new kernel shutdown -r now ### Set up CARP With the CARP kernel module installed, setting up CARP is super easy. All you need to do to add two lines in rc.conf on each node. We will create a virtual IP address (192.168.10.10) that automatically points to the primary node (or secondary node if the primary is down). We use the password ‘MyPassword’ for the session. The primary node has an advskew value set to 10 and the secondary node to 20. CARP will automatically use the node with the lowest advskew as the primary node. On the primary node, add this to /etc/rc.conf: cloned_interfaces="carp0" ifconfig_carp0="vhid 1 pass MyPassword advskew 10 192.168.10.10 netmask 255.255.0.0" hastd_enable="YES" And then run the following (the preempt makes the primary node the default primary): ifconfig carp0 create sysctl -w net.inet.carp.preempt=1 echo -e "\\nnet.inet.carp.preempt=1" >> /etc/sysctl.conf On the secondary node, add this to /etc/rc.conf: cloned_interfaces="carp0" ifconfig_carp0="vhid 1 pass MyPassword advskew 20 192.168.10.10 netmask 255.255.0.0" hastd_enable="YES" And run: ifconfig carp0 create if you run ‘ifconfig carp0′ on either node, you will see if it is the primary or secondary node. Here’s from my secondary node. carp0: flags=49 metric 0 mtu 1500 inet 192.168.10.10 netmask 0xffff0000 carp: BACKUP vhid 1 advbase 1 advskew 20 You should also be able to ping 192.168.10.10 from a different host and have the primary node respond to the ping. If you restart the primary node, the secondary node should automatically be promoted to the primary node (eg. you can ping 192.168.10.10 from a different host and only lose one or two packages when you reboot the primary node). ### Configure and set up HAST Configuring HAST is also pretty straight forward. In my case, I have two nodes: s1 and s2. They both have a separate disk (ad6) that I will dedicate to HAST. s1 has the IP address 192.168.10.11 and s2 192.168.10.12. Also make sure that you have these hosts added in /etc/hosts (or in your local DNS). As you can see in my hast.conf, I decided to call my HAST pool ‘hast0′. Now create the file /etc/hast.conf with the following on both nodes: resource hast0 { on s1 { local /dev/ad6 remote 192.168.10.12 } on s2 { local /dev/ad6 remote 192.168.10.11 } } Once you’ve created the file on both nodes, you need to run the following on each node (replace hast0 if you named it something else in hast.conf): hastctl create hast0 hastd Now move over to your primary node, and run the following command: hastctl role primary hast0 On the secondary node, run the following command: hastctl role secondary hast0 Verify the result by running the following on each node: hastctl status hast0 Pay attention to the ‘status’ line. It should say ‘complete’ on both sides. If it says ‘degraded,’ you’ve done something wrong. Lastly, we need to create a filesystem for HAST. On the primary node, run: newfs -U /dev/hast/hast0 Depending on the size of your disk/partition, this may take a few minutes. Once it is done, you should be able to mount your HAST pool with something like this: mkdir /mnt/hast0 mount /dev/hast/hast0 /mnt/hast0 It is also likely that you will see that ‘hastd’ will consume 10-20% of your CPU on both nodes as it replicates the data across the systems for the initial sync. You know that it has replicated the data completely when ‘hastctl status’ on the primary node reports 0 bytes of ‘dirty’. Again, depending on the size of your disk/partition, this can take a long time (perhaps hours if you have a large drive). I suggest you do not move forward in this guide until you have your disks completely in sync. ### Configure the failover Now we need to configure HAST and CARP to work together. What we want to accomplish is if the primary node goes down, the secondary node should take over seamlessly. To do this, we will use ‘devd.’ We will use the carp0 link up and down status as the trigger. Open /etc/devd.conf and add the following on both nodes: notify 30 { match "system" "IFNET"; match "subsystem" "carp0"; match "type" "LINK_UP"; action "/usr/local/sbin/carp-hast-switch master"; }; notify 30 { match "system" "IFNET"; match "subsystem" "carp0"; match "type" "LINK_DOWN"; action "/usr/local/sbin/carp-hast-switch slave"; }; When the link goes up or down, we call on the script carp-hast-switch. You can find the script [here](https://vpetersson.com/upload/carp-hast-switch). The only thing you should need to modify is the ‘resources’-line. Here are the commands for fetching the file, if you’re lazy: wget [https://vpetersson.com/upload/carp-hast-switch](https://vpetersson.com/upload/carp-hast-switch) -O /usr/local/sbin/carp-hast-switch chmod +x /usr/local/sbin/carp-hast-switch When you’ve added the dev.d configs, restart the devd demon on both nodes: /etc/rc.d/devd restart Here’s what the script does when a node becomes master (or primary): - Promotes itself to primary in to HAST - Mount the HAST disk (eg. /mnt/hast0) Here’s what the script does when it becomes slave (or secondary) - Umounts the HAST disk - Degrades itself to secondary in HAST That’s it. You should now be able to restart the primary node, and the secondary node should automatically be promoted to the primary node. You can now configure your two nodes to share the HAST disk (/mnt/hast0) using Samba or NFS with the shared IP address. **Credits**: [Michael W. Lucas](http://blather.michaelwlucas.com) for the HAST master/slave script. --- # How to build Apache and mod_wsgi with Python 2.7 on FreeBSD URL: https://vpetersson.com/2010/09/08/how-to-build-apache-and-modwsgi-with-python-2-7-on-free/ Date: 2010-09-08 We’re probably not the only company switching to Python 2.7. Right now, we’re in the final phase of rolling out an updated version that uses Python 2.7. As I was setting up our servers, I ran into a few issues with packages who were hardcoded to use Python 2.6 or earlier. Both [Chronicle](http://www.chronicle.im) and [YippieMove](http://www.yippiemove.com) are using Django, and use on Apache with mod_wsgi. When building these two packages, we found out that were both hardcoded to use Python 2.6 or earlier. Fortunately, there’s a simple solution for it. The first package is ‘dev/apr1′, one of Apache’s dependencies. To resolve this issue, simply edit _‘/usr/ports/dev/apr1/Makefile’_. Find the line that says: > USE\_PYTHON\_BUILD= -2.6 and replace it with: > USE\_PYTHON\_BUILD= -2.7 The second application is mod\_wsgi (www/mod\_wsgi) itself. To resolve this edit ‘_/usr/ports/www/mod_wsgi/Makefile_‘ and change the line: > USE_PYTHON= 2.4-2.6 to > USE_PYTHON= 2.4-2.7 Both packages will compile just fine with Python 2.7, so no worries. I have notified both maintainers, but in the meantime, the above fix should do. Also, don’t forget to run ‘make clean’ before you try to rebuild the packages. Just for the record, we’re running FreeBSD 8.1 (AMD64), but that shouldn’t matter for the issue above. --- # How to get RabbitMQ 1.8 to work on FreeBSD URL: https://vpetersson.com/2010/08/31/how-to-get-rabbitmq-1-8-to-work-on-freebsd/ Date: 2010-08-31 **Update:** Thanks to Phillip (the maintainer of the package), this issue has now been resolved for RabbitMQ 2.0. The instructions below still applies if you for some reason prefer to run RabbitMQ 1.8. This post might be irrelevant as soon as the port maintainer resolves this issue, but as I’m writing this, th**is bug will prevent you from running** RabbitMQ successfully. Installing RabbitMQ is simple, but there are a few tricks that you might want to keep in mind. Before we build RabbitMQ, let’s build Erlang. cd /usr/ports/lang/erlang make config Make sure you deselect Java, VX and X11, as you don’t need them if you’re only planning to run Rabbit. make install Now, let’s build Rabbit: cd /usr/ports/net/rabbitmq make install If you are getting errors when compiling RabbitMQ, it could be due to a problem with ‘docbook’ (at least that happened to me). The error I got said something about docbook and XML. To resolve that, recompile docbook (/usr/ports/textproc/docbook) with all options available (run ‘make config’ and check everything). I know it’s not a very scientific approach, as I should have dugg out what actual dependency it was, but it did the tric. Assuming everything went well, all you need to do is to add “rabbitmq_enable=”YES”” to /etc/rc.conf echo -e “\\nrabbitmq_enable=\\”YES\\”” >> /etc/rc.conf Now we can start RabbitMQ: /usr/local/etc/rc.d/rabbitmq start Finally, we need to fix the bug that I was referring to in the opening of the article. To do this, we need to get the rabbitmqctl-file from RabbitMQ 2.0. I’ve uploaded extracted it for you and made it available for download, but you can as well get it from the tar-ball [here](http://www.rabbitmq.com/releases/rabbitmq-server/v2.0.0/rabbitmq-server-generic-unix-2.0.0.tar.gz). cd ~ fetch [https://vpetersson.com/upload/rabbitmqctl](https://vpetersson.com/upload/rabbitmqctl) chmod +x rabbitmqctl mv /usr/local/sbin/rabbitmqctl /usr/local/sbin/rabbitmqctl.bak mv rabbitmqctl /usr/local/sbin/rabbitmqctl You should now be able to use RabbitMQ. To create a user, simply run: sudo -H -u rabbitmq rabbitmqctl add_user username password Unfortunately there is still another bug that prevents you from running “/usr/local/etc/rc.d/rabbitmq stop”, but I haven’t found a solution for that as I’m writing this. **Credit**: Thanks to blunt_ from #rabbitmq on FreeNode --- # Shouldn't dependencies of core components be isolated? URL: https://vpetersson.com/2010/08/28/shouldnt-dependencies-on-core-components-be-isolated/ Date: 2010-08-28 Local management tools are critical for most Linux and Unix distributions. For instance, if you delete Python 2.6 from your Ubuntu installation, it becomes more or less unusable. This is because most local management tools are written in Python. I have no problem with this. On the contrary, I think it makes a whole lot of sense to write management tools in a high-level language, such as Python or Ruby. The problem is that there are many circumstances in where you’d would need to install a different version of these languages, as some other tool or application you’re using requires it. This is likely to cause problems. It is particularly true if you’re running an LTS-version of Ubuntu, or CentOS/RHEL (which is still using Python 2.5). Yes, you can run multiple versions of Python on the same machine, but it’s quite likely that applications will be confused on what version to use. Also, what version should you point the command ‘python’ to? Yes, you can call on Python with it’s full name (ie python26), but ‘python’ is still what many scripts call on. To resolve this problem, why don’t we isolate these components into it’s own environment? On FreeBSD, ‘jails’ could be used and on Linux, perhaps ‘chroot’ can be used. Then these management tools can run within this environment and just use some kind of bridging tool to connect it to the user environment. That way it doesn’t matter what kind of version that the base-system is running, because the core components are isolated anyways. While I reckon that this would take up extra disks space, but for everything but embedded systems, 100 MB (or whatever it would take), isn’t a big deal. I’m not sure if I’m onto something here, or if I’m just rambling. Just though it’s an interesting idea, and it was worth putting out there. --- # Solution for "[Errno 13] Permission denied: '/nonexistent'" in mod_wsgi URL: https://vpetersson.com/2010/08/26/solution-for-errno-13-permission-denied-nonexistent-in-m/ Date: 2010-08-26 While upgrading to Python 2.7 on one of our development servers (FreeBSD 7.2), I ran across a somewhat strange error with Django (or rather mod_wsgi). Since I didn’t find a whole lot useful results when I Googled for it, I decided to do a brief write-up about it. The error I received was as follows: > \[Errno 13\] Permission denied: ‘/nonexistent’ As it turns out ‘/nonexisten’ is the home-directory for ‘www’ on FreeBSD. This is good, as you want your webserver to have as little write-access as possible. The problem is that Python uses something called Egg-files for its modules. These can be either stored extracted or compressed. The compressed ones are basically just a zip-file with the .egg-extension. The above error originates from when mod_wsgi tries to extract one of these .egg-files in the home-directory. Since ‘/nonexistant’ is non-existing folder (shocking, right?), it fails. Some people out there suggest that you change www’s home-directory and give it write-access. I suggest a different approach: Extract the compressed egg-files. I wrote a simple Bash-script to take care of the task: > #!/bin/sh > > \# The path to your site-packages directory.\ > SITEPACKAGES=/usr/local/lib/python2.7/site-packages > > for i in $(find $SITEPACKAGES -type f -maxdepth 1 |grep .egg |grep -v egg-info); do\ > unzip $i -d $SITEPACKAGES\ > rm -rf $SITEPACKAGES/EGG-INFO\ > rm -f $i\ > done; It’s not very pretty, but it gets the job done. If you think the second ‘grep’ for ‘egg-info’ looks weird, it’s to exclude .egg-info files. You don’t need to extract them, as it’s not the “real” module. Personally I don’t really see the reason for modules to compress .egg files. I just can’t see the justification for saving a few bytes (these modules are usually very small) and instead having to waste extra CPU and I/O when extracting these files at run. Anyways, that’s how I solved it the problem and it did the trick for me. And as always, if you manage to screw the above script up and it wipes all your data, don’t come to me crying. Running script as root (or with sudo) is dangerous and you should always be careful. --- # Can VirtualBox take on VMware for SMBs? URL: https://vpetersson.com/2010/08/22/can-virtualbox-take-on-vmware-for-smbs/ Date: 2010-08-22 My experience with VMware goes way back. I think the first version I ever used was VMware Workstation 4.0 back in ’03. That’s seven years ago. Back then it was really cool as a proof-of-concept, but not very useful as the hardware didn’t have enough power (primarily RAM) to run multiple OS’es simultaneously (or, at least **my** hardware). A few years ago I started to use VMware more seriously. [VMware Server](http://www.vmware.com/products/server/) was great. It ran on Linux and was pretty flexible. It lacked a few features (such as multiple snapshots), but it did the job. When we first launched [YippieMove](http://www.yippiemove.com), we actually ran the entire architecture with a few VMware Servers. It worked, but due to budget hardware, it didn’t perform as well as we liked it to. (We eventually switched to FreeBSD Jails and [the article we wrote](http://www.playingwithwire.com/2009/06/virtual-failure-yippiemove-switches-from-vmware-to-freebsd-jails/) about it made it to Slashdot.) As time elapsed, a few things started to bug me more and more with VMware Server. The 2.0-release resolved some of these, as it introduced a web-interface, and you no longer needed “VMware Console” to manage the virtual machines. Yet, the web interface is pretty buggy. The web interface crashes quite frequently, and you need to reload the entire window. Sometimes the login screen won’t show up at all, and you need to reload the window like 10 times before it shows up. **Perhaps the most frustrating thing with VMware Server is the lack of OS X support for the console** (which is a Firefox plug-in). In order to access the console, you need to be either on Windows or Linux. Hence I need to fire up VMware Fusion with a Linux guest-OS or remotely connect to a Windows machine in order to access the console. Very frustrating to say the least. There are also other frustrating issues, such as the incompatibility between VMware Fusion and VMware Server. ![VMware_Server_web_interface](https://vpetersson.com/wp-content/uploads/2010/08/VMware_Server_web_interface-600x278.png "VMware_Server_web_interface")\ _VMware Server 2 in action._ Since the release of VMware Server 2.0, there have been two bug-fix releases. The latest version, 2.0.2, was released on October 26, 2009. That’s over 9 months ago. I definitely reckon that VMware do not put a whole lot of engineering resources into a free product. The rational for giving away VMware Server for free is supposedly that it is a stepping stone for SMBs into VMware ESX. That makes sense. When you grow out of VMware Server, you can simply take all your existing Virtual Machines and plug them straight into ESX, and get features like clustering with failover. Given that VMware is more or less the industry standard for virtualization, why would you want to use anything else? The above issues with VMware Server, as well as a very slow release cycle (perhaps even frozen development), have lead me to research alternatives. Xen is an obvious alternative. If it can power Amazon’s EC2, it obviously scales well. Yet, it falls short of a pretty important criteria for me: Platform independence. I want to be able to create a virtual machine on my local machine, and then move it to the server when it is done. Xen only works on Linux as a host OS. (The FreeBSD guest-OS is also very experimental). I also find it surprising that there is no real good web-based management tool for Xen (or perhaps I just failed to find it). There are a ton of ‘projects’ but no clear choice. With Xen out of the picture, we’re only left with the newcomer: VirtualBox. The Open Source Edition is more or less at par with VMware Server when it comes to features (eg. VirtualBox can do multiple snapshots, but VMware Server can handle USB pass-trough). VirtualBox runs on most operating systems (Mac, Linux, Windows and even FreeBSD). The deal breaker for me used used to be the lack of web-interface in VirtualBox. Then along came [phpVirtualBox](http://code.google.com/p/phpvirtualbox/). It’s open source and uses simple components, such as PHP to display the data (VMware Server uses Tomcat, which is pretty much the opposite of lightweight). phpVirtualBox also supports access to the console directly in the browser. You’re no longer restricted to Firefox on Windows and Linux. ![phpVirtualBox](https://vpetersson.com/wp-content/uploads/2010/08/phpvbsm-600x445.png "phpVirtualBox")\ _phpVirtualBox in action._ **Let’s revisit the original question**: Can VirtualBox take on VMware for the SMB market? I definitely think so. The Open Source Edition of VirtualBox is very responsive and as far as features goes I’d say it is at par with VMware Workstation as a stand-alone machine. Throw in phpVirtualBox to the mix, and I’d say it is at par with VMware Server. Since VMware have more or less ceased the development of VMware Server, it could be a good time to switch. Yet, this assumes that you do not need features like clustering, failover and disaster recovery etc. If you only need a few headless boxes to run various services on, VirtualBox can do the job for you, and probably better than VMware Server does today. The only question mark that pops up in my head with VirtualBox is: What is Oracle’s strategy with VirtualBox? There is a commercial fork of VirtualBox, but there are very little data available about it on VirtualBox’s website (other than [an outline of the difference](http://www.virtualbox.org/wiki/Editions) between the two editions). You can download the commercial version for free for personal use, but there is no pricing available for commercial use. That said, VirtualBox Open Source Edition was released under GPL, so regardless of what Oracle decides to do with VirtualBox, the source code is still there for developers to fork their own release. --- # How monkeys mirror human irrationality URL: https://vpetersson.com/2010/08/18/how-monkeys-mirror-human-irrationality/ Date: 2010-08-18 I watched a really interesting [TED](http://www.ted.com/)-talk last night that I wanted to share with all of you. It’s on the topic of human irrationality and how it is mirrored in monkeys. The verdict is that monkeys make the same irrational decisions as humans make, despite the fact that we know they’re irrational decisions.

Here’s the video:

--- # How to avoid monthly service fees with Wells Fargo (Business and Personal) URL: https://vpetersson.com/2010/08/03/how-to-avoid-monthly-service-fees-with-wells-fargo-busin/ Date: 2010-08-03 I will rant a bit about how pathetic the U.S. banks are, so if you don’t want to read about that, jump down to ‘End Rant.’ ### Rant Banks in the US are kind of like the movie industry. They realize that their golden days are over, and will therefor try to squeeze every penny out of you. The two industries are also in denial about the fact that the entire world have gone online and that if they don’t adopt, they will slowly die. Online banking in the U.S. is a joke. In the year 2010, I can still not transfer money between two different banks online. I can’t even do a wire transfer online. Well, maybe that’s a good thing, given the pathetic security. Your bank records are not more secure than the average web service. ### End Rant Have you ever seen a message saying “MONTHLY SERVICE FEE” on your banking statement? If you bank with Wells Fargo, chances are you have. Luckily, there is a simple fix for it. All you need to do is to set up a scheduled transfer from your checking to your savings-account and back every month. That’s it. For a personal account, you will need to transfer $75 back and forth, and for business it’s $100. Pretty silly, I know, but it’s well worth a few minutes to save you a few lattes a month. Here’s how you do it. Log into your account, click “Schedule an automatic transfer” in the left bar under “Transfers & Payments.” Once there, just schedule the transfer as described above. When done, the result should look something like this:\ ![Wells Fargo Monthly Fee](https://vpetersson.com/wp-content/uploads/2010/08/wellsfargo-600x201.png "Wells Fargo Monthly Fee") **Note**: I can not guarantee that this works in any way. It works for me, and I have an account with Well Fargo in California. **Update 2**: I’m pretty confident this doesn’t work anymore. --- # Create a lightweight intranet search engine with Xapian on FreeBSD URL: https://vpetersson.com/2010/08/01/create-a-lightweight-intranet-search-engine-with-xapian/ Date: 2010-08-01 Recently I had to set up an intranet search engine to crawl trough thousands of PDF files. There are a ton of commercial solutions (read: $$$$ ) out there on the market, ranging from [Google Search Appliance](http://www.google.com/enterprise/search/gsa.html) to [IBM’s OmniFind](http://omnifind.ibm.yahoo.net/). There are also a few good Open Source engines, such as [Apache’s Lucene](http://lucene.apache.org/java/docs/index.html). The problem is that these are primarily intended for enterprises with server farms full of data. That’s really not what I was looking for. I was looking something simple that was easy to set up and maintain. That’s when I came across [Xapian](http://xapian.org/). It’s Open Source and lightweight. Combine Xapian with [Omega](http://xapian.org/docs/omega/overview.html) and you got exactly what I was looking for — A lightweight intranet search engine. This howto will walk you trough how to set up Xapian with Omega on FreeBSD. The version I used was FreeBSD 8.1, but I’m sure any recent version of FreeBSD (7.x>) will do. Please note that I do expect you to know your way around FreeBSD, so I’m not going to spend time on simple tasks like how to edit files etc. I also assume you already got your system up and running. I’ve called the path we’re going to index (recursively) ‘/path/to/something’. This can be either a local path or something mounted from a remote server. Also, as you’ll see below, a lot of dependencies are installed. This is to increase the number of file-format Xapian will index. It should be able to index PDF-files, Word-files, RTF-files, in addition to plain-text files. Let’s get started. **Note**: If you don’t have the ports-tree installed (/usr/ports), you can download it by simply running: portsnap fetch extract **Install Apache** /usr/ports/www/apache22 make install echo -e “\\napache22_enable=\\”YES\\”” >> /etc/rc.conf **Install Xapian with Xapian-Omega** cd /usr/ports/www/xapian-omega make install **Install Xpdf**\ Make sure to uncheck X11 and DRAW cd /usr/ports/graphics/xpdf make install **Install Catdoc**\ Uncheck WORDVIEW cd /usr/ports/textproc/catdoc make install **Install Unzip** cd /usr/ports/archivers/unzip make install **Install Gzip** cd /usr/ports/archivers/gzip make install **Install Antiword** cd /usr/ports/textproc/antiword make install **Install Unrtf** cd /usr/ports/textproc/unrtf make install **Install Catdvi** cd /usr/ports/print/catdvi make install Next we need to edit Apache’s config-file (/usr/local/etc/apache22/httpd.conf) Change: ScriptAlias /cgi-bin/ “/usr/local/www/apache22/cgi-bin/” Into: ScriptAlias /cgi-bin/ “/usr/local/www/xapian-omega/cgi-bin/” We also need to create a new config-file for Xapian. Create the file /usr/local/etc/apache22/Include/xapian.conf Alias /something /path/to/something Options Indexes AllowOverride None Order allow,deny Allow from all AllowOverride None Options None Order allow,deny Allow from all With all Apache configuration being done, let’s fire up Apache: /usr/local/etc/rc.d/apache22 start Create the holding directory mkdir -p /usr/local/lib/omega/data/ Copy over the templates. For some reason FreeBSD doesn’t do this by default. cp -rfv /usr/ports/www/xapian-omega/work/xapian-omega-*/templates /usr/local/lib/omega/ We also need to tell Xapian-Omega where to look for the files. Create the file /usr/local/www/xapian-omega/cgi-bin/omega.conf \# Directory containing Xapian databases: database_dir /usr/local/lib/omega/data \# Directory containing OmegaScript templates: template_dir /usr/local/lib/omega/templates \# Directory to write Omega logs to: log_dir /var/log/omega \# Directory containing any cdb files for the $lookup OmegaScript command: cdb_dir /var/lib/omega/cdb Create a search page. I’ll just use index.html in Apache’s default DocumentRoot (/usr/local/www/apache22/data/index.html). Match any word Match all words --- # Amazon S3/Jungle Disk as your home NAS? URL: https://vpetersson.com/2010/08/01/amazon-s3jungle-disk-as-your-home-nas/ Date: 2010-08-01 This idea hit me this morning. Assuming you have a decent connection at home (not ADSL or Cable that is), [Amazon S3](http://aws.amazon.com/s3/) (or [Jungle Disk](https://www.jungledisk.com)) makes a pretty nice back-bone for a home NAS. It is fairly cheap and you will no longer worry about growing out of your array or failing disks. Yes, I reckon that if you store your data without encryption (even in a private bucket), it may leak out. However, as long as you’re not storing top-secret government files, I think you’ll be fine. While you could just use something like [Transmit](http://panic.com/transmit/) on your Mac to mount the S3 share locally, it’s not ideal for home network if you have multiple machines. Instead, we can set up a simple server (virtual or physical) to act as a gateway to the remote storage. Here’s what I’m thinking: - Install Ubuntu (or your favorite Linux distribution) on a server (virtual or physical) - Install [s3fs](http://code.google.com/p/s3fs/) if you’re S3 or the Jungle Disk for Linux. If you’re using S3, create a private bucket. I’m not sure how that works on Jungle Disk. - Mount the remote drive to something like /shared - Install and configure Samba to share /shared to the local network Simple as that. You can now access the S3/Jungle Disk share as you would with a regular physic NAS. Granted, I haven’t tried this myself, but it should work in theory at least. The only problem I can foresee is the latency issue. Also, you can obviously not expect LAN speed to the storage back-end, but if you have a decent connection you should be able to get at least a few MB/sec. That should be sufficient for browsing pictures and even stream a (non HD) movie. As a bonus, you can just copy the virtual machine you set up above to another network and have access to the same files from there. --- # How to backup to S3 with GnuPG (PGP) without having to store the passphrase locally URL: https://vpetersson.com/2010/07/23/how-to-backup-to-s3-with-gnupg-pgp-without-having-to-sto/ Date: 2010-07-23 To increase the reliability of our backups at WireLoad, we wanted to utilize S3. Obviously we couldn’t just send our backups to S3 without encrypting them, so GnuPG was part of the equation from the beginning. As I started my research, I found a ton guides on how to utilize a variety of backup tools to get your backups delivered to S3. Some of the tools looked really promising. After reading the specs, [Duplicity](http://duplicity.nongnu.org/) stood out as the winner. It supported S3, encryption and the whole shebang. It even supported incremental backups. Bingo I thought. That’s perfect. That said, I installed Duplicity on a test-server and started experimenting with it. As I’m fairly familiar with GnuPG and PGP encryption, I reckoned that the ideal setup would be the standard public/private key structure and only have the public key installed on the server. The private key would be stored elsewhere. So far so good, Duplicity asked for the public key in its configure, but it still asked for the passphrase when running. Surely, you could store the passphrase in plain text and parse it to Duplicity, but that’s kind of pointless, as it defeats the purpose of a passphrase. Now you may say ‘Hey! you probably already store the backups without encryption anyways, so what’s the point?’ The point is that there could be backups from other servers stored in the S3 bucket. If you save the passphrase (and somehow gets your hand on the private key), these are all compromised too. If you were instead able to encrypt the backup just using the public key, without the passphrase, these backups would be a lot more secure. As it turns out, that is possible with GnuPG — just not with Duplicity (AFAIK). The key is to encrypt the backups with the public key as the ‘recipient.’ I’m not going to tell you how to get up and running with GnuPG (see [this page instead](http://www.gnupg.org/gph/en/manual/c14.html)). Assuming you’ve installed the public key (gpg –import your-public-key), all you need to for encrypting a file without entering the passphrase is as follows: gpg –encrypt –recipient ‘the-email-in@your-key.net’ filename Pretty easy, right? If we want to pull that into a script, we just need [s3cmd](http://s3tools.org/s3cmd) to the mix, and we’re pretty much all set. You will also need to configure s3cmd (s3cmd –configure) with your AWS information. Here’s the script I ended up with. Please note that the path I backup only includes achieves generated from another backup script. Also, the archive only includes files with the extension tbz, bz2 and tar. I’m sure you could play with s3cmd’s ‘rexclude’ feature to write a pretty RegEx, but it wasn’t worth the time for me. #!/bin/sh \# Path to backup BACKUP=/path/to/backup/archive for i in \`find $BACKUP -type f | grep -v .gpg\` do gpg –batch –encrypt –recipient ‘the-email-in@your-key.net’ $i done \# S3 only allows 1GB files. To resolve that we use ‘split’ to break files larger than 1GB apart. \# When done splitting, we truncate the original file to save diskspace. We cannot delete it, as gpg will then re-create it next run. for j in \`find /path/to/backup/archive -size +1G |grep gpg\`; do split -a 1 -b 1G $j $j- truncate -s 0 $j done; s3cmd sync -r $BACKUP –exclude=’.tbz’ –exclude=’.bz2′ –exclude=’.gz’ s3://your-bucket/\`hostname -s\`/ Enjoy. If you have any comments, please let me know. **Update:** Added ‘split’ to work around S3′s 1GB file limitation. --- # Chronicle.IM is almost ready... URL: https://vpetersson.com/2010/07/17/chronicle-im-is-almost-ready/ Date: 2010-07-17 Last night we rolled out a new version of [Chronicle.IM](http://www.chronicle.im) to the production servers. The product is not quite ready yet, but you can sign up with your email address if you want to be invited to the beta-version. Over the next few weeks we will start letting a few beta-testers in. If everything goes well, we will invite more. So what is [Chronicle.IM](http://www.chronicle.im)? The slogan for the product is ‘Write the story of your life.’ That sums it up pretty well. It is an online journaling/diary app that allows you to keep track of your life in with ease and style. While I don’t want to reveal all the details now, let’s just say that we’ve focused on simplicity and usability. You won’t be disappointed. --- # Monitor Nginx and disk-usage with Monit URL: https://vpetersson.com/2010/07/12/monitor-nginx-and-disk-usage-with-monit/ Date: 2010-07-12 Yesterday I posted an article on [how to monitor Apache and PostgreSQL with Monit](https://vpetersson.com/2010/07/09/setting-up-monit-to-monitor-apache-and-postgresql-on-ubuntu/). After setting that up I was amazed how simple and flexible Monit was, so I moved on with two more tasks: monitor Nginx and disk usage. This article assumes that you’ve set up Monit in accordance with the previous article. It also assumes that you’re on Ubuntu 9.10 or 10.04. If you use a different Linux or Unix flavor, you will probably need to modify a few paths. #### Nginx (/etc/monit/conf.d/nginx.conf) > check process nginx with pidfile /var/run/nginx.pid\ > group www\ > start program = “/etc/init.d/nginx start”\ > stop program = “/etc/init.d/nginx stop”\ > if children > 250 then restart\ > if loadavg(5min) greater than 10 for 8 cycles then stop\ > if 3 restarts within 5 cycles then timeout [Download](https://vpetersson.com/upload/monit/nginx.conf) #### Disk usage (/etc/monit/conf.d/diskusage.conf) > check filesystem md3 with path /dev/md3\ > group server\ > if failed permission 660 then unmonitor\ > if failed uid root then unmonitor\ > if failed gid disk then unmonitor\ > if space usage > 80 % then alert\ > if inode usage > 80 % then alert [Download](https://vpetersson.com/upload/monit/diskusage.conf) Please note that the above script monitors ‘/dev/md3.’ You can replace this with the path to the file system you want to monitor, such as /dev/sda1 or /dev/hda1. You can also configure Monit to take actions based on certain criterions, but I left that part out as I didn’t find any need for it. You can read more about that [here](http://mmonit.com/monit/documentation/monit.html). If you want to monitor more file systems, simply repeat the above config with a different ‘path.’ Once you’ve added you new configs, you need to restart Monit (/etc/init.d/monit restart). --- # Setting up Monit to monitor Apache and PostgreSQL on Ubuntu URL: https://vpetersson.com/2010/07/09/setting-up-monit-to-monitor-apache-and-postgresql-on-ubu/ Date: 2010-07-09 [Monit](http://mmonit.com/monit/) is a great little utility that monitors your daemons. If a daemon fails, Monit will start the daemon it will automatically restart the process. It comes in very handy if for web-servers, such as Apache. For [Red iGone](http://www.redigone.com) we use Apache as the web-server, and PostgreSQL as the database. I wanted to configure Monit to keep an eye on these processes. As it turns out, setting up Monit was really straight-forward. I tried this on Ubuntu 9.10 and 10.04. If you try this on a different Ubuntu version (or other distribution), it is likely that you will need to make changes to apache.conf and postgresql.conf. ![](https://farm5.staticflickr.com/4082/4777587884_23d7aa9958_z_d.jpg) _Monit in action._ ### Install Monit Installing Monit on Ubuntu is dead simple. Just run: sudo apt-get install monit ### Configure Monit Now let’s configure Monit. We start with the generic config-file. Open **/etc/monit/monitrc** in your favorite editor and add the following line: include /etc/monit/conf.d/* **Important**: Change permission on the folder /etc/monit/conf.d as it will include your email password stored in plain text. sudo chmod a-rwx,u=rwX -R /etc/monit/conf.d/ Next we need to edit **/etc/default/monit** and change “startup=0″ to “startup=1″. Now we’re ready to really start configuring Monit. Just to keep things, organized, I’ve broken down the Monit’s settings into three files: - basic.conf - apache.conf - postgresql.conf In basic.conf I’ve put the generic Monit-configs, and then broken out Apache’s and PosgreSQL’s configs into their own files. #### basic.conf (/etc/monit/conf.d/basic.conf) > set daemon 60\ > set logfile syslog facility log_daemon > > set mailserver smtp.gmail.com port 587\ > username “user@domain.com” password “password”\ > using tlsv1\ > with timeout 30 seconds > > set alert admin@domain.com > > set httpd port 2812 and\ > use address localhost\ > allow localhost\ > allow admin:monit > > check system localhost\ > if loadavg (1min) > 4 then alert\ > if loadavg (5min) > 2 then alert\ > if memory usage > 75% then alert\ > if cpu usage (user) > 70% then alert\ > if cpu usage (system) > 30% then alert\ > if cpu usage (wait) > 20% then alert [Download](https://vpetersson.com/upload/monit/basic.conf) The above file is configured to send email using Google Apps or Gmail. Just change your username and password according to your needs. I’ve also enabled Monit’s webserver that allows you to view Monit’s status directly in your browser. Access is restricted to localhost and you need to login with the username ‘admin’ and password ‘monit. I prefer to restrict access to just localhost, and then use a SSH-tunnel to gain access. #### apache.conf (/etc/monit/conf.d/apache.conf) > check process apache2 with pidfile /var/run/apache2.pid\ > group www\ > start program = “/etc/init.d/apache2 start”\ > stop program = “/etc/init.d/apache2 stop”\ > if children > 250 then restart\ > if loadavg(5min) greater than 10 for 8 cycles then stop\ > if 3 restarts within 5 cycles then timeout [Download](https://vpetersson.com/upload/monit/apache.conf) This is pretty straight forward. Assuming you use the Apache-distribution that came with Ubuntu, you shouldn’t need to modify anything. #### postgresql.conf (/etc/monit/conf.d/posgresql.conf) > check process postgresql with pidfile /var/run/postgresql/8.4-main.pid\ > group database\ > start program = “/etc/init.d/postgresql-8.4 start”\ > stop program = “/etc/init.d/postgresql-8.4 stop”\ > if failed unixsocket /var/run/postgresql/.s.PGSQL.5432 protocol pgsql then restart\ > if failed unixsocket /var/run/postgresql/.s.PGSQL.5432 protocol pgsql then alert\ > if failed host localhost port 5432 protocol pgsql then restart\ > if failed host localhost port 5432 protocol pgsql then alert\ > if 5 restarts within 5 cycles then timeout [Download](https://vpetersson.com/upload/monit/postgresql.conf) The same thing applies here. Assuming you used the PosgreSQL-version (8.4) that came with Ubuntu, you shouldn’t need to modify anything here. ## Final steps Now that you have configured all the files, all that needs to be done is to fire up Monit and make verify that it is running. To launch Monit, simply run: sudo /etc/init.d/monit start You can verify that Monit is running either by browsing to the webserver or checking /var/log/syslog. You should also receive an email that says that Monit is now running. Assuming everything went well, you also want to make sure that Monit actually starts a daemon if it is failing. A simple way to do that is to run: sudo killall apache2 That should kill Apache. Monit should be able to detect that and fire it back up shortly. Again, you should be able to monitor Monit’s process either by email, the web-interface, or in /var/log/syslog. That’s it. Good luck! **Update:** I decided to extend this guide and wrote another article on [how to monitor Nginx and disk-usage](/2010/07/12/monitor-nginx-and-disk-usage-with-monit/) with Monit. --- # Website review session from Google I/O URL: https://vpetersson.com/2010/06/04/website-review-session-from-google-io/ Date: 2010-06-04 If you are new to SEO, or just want to learn more about SEO, Matt Cutts and a few colleagues did a really good presentation on Google I/O where they went through several websites and reviewd them from an SEO perspective. The whole presentation is about an hour long, but well worth watching.

--- # How to install ZoneMinder 1.24.2 on Ubuntu 10.04 LTS Server URL: https://vpetersson.com/2010/05/23/how-to-install-zoneminder-1-24-2-on-ubuntu-10-04-lts-ser/ Date: 2010-05-23 Last week I published a new version of my [ZoneMinder Virtual Appliance](https://vpetersson.com/open-source/zoneminder-virtual-appliance). The virtual appliance is great if you want to easily deploy ZoneMinder without having to spend time setting it up. However, in some situations, you want to run ZoneMinder directly on the hardware. Perhaps you need better performance or simply need to capture video streams from V4L-devices. Since I already spent the time getting it running, I thought I’d share the instructions for getting it running. It’s pretty straight forward, but there are a few minor things that took me some time to get around. \[flickr-gallery mode=“photoset” photoset=“72157624119100112”\] # Installation **Optional:** I personally prefer to install the ‘minimal’ version of Ubuntu. You can install this mode by simply hitting F4 right at boot. Other than the installing the ‘minimal’ system, the only things you would need to keep in mind are to install “LAMP” and “OpenSSH” under the Software selection. You will also need to pick a MySQL password, which will be used later. # Configuration Upgrade the package repository: sudo apt-get update && sudo apt-get upgrade Install all required dependencies: sudo apt-get install build-essential ffmpeg libmysqlclient-dev libjpeg-dev libssl-dev libdate-manip-perl wget liblwp-useragent-determined-perl libavformat-dev libphp-serialization-perl libswscale-dev joe Get Zoneminder: wget [http://www.zoneminder.com/downloads/ZoneMinder-1.24.2.tar.gz](http://www.zoneminder.com/downloads/ZoneMinder-1.24.2.tar.gz) Extract Zoneminder and change permission: sudo tar xvfz ZoneMinder-1.24.2.tar.gz -C /usr/local/ sudo chown -R $(whoami) /usr/local/ZoneMinder-1.24.2 Configure Zoneminder: cd /usr/local/ZoneMinder-1.24.2 ./configure --with-webdir=/var/www/zm --with-cgidir=/usr/lib/cgi-bin/ --with-webuser=www-data --with-webgroup=www-data ZM\_DB\_USER=zm ZM\_DB\_NAME=zm ZM\_DB\_PASS=yourpassword ZM\_SSL\_LIB=openssl Resolve a bug (discussed more [here](http://www.zoneminder.com/forums/viewtopic.php?p=55152)): joe src/zm_utils.cpp (or your favorite editor)\ Add the line ‘#include ‘ on row 22 (or somewhere in that general area). To exit and save with Joe, press _ctrl+k x_. Build and install Zoneminder make sudo make install Configure the database: mysql -uroot -p Install Cambozola: cd wget [http://www.charliemouse.com:8080/code/cambozola/cambozola-latest.tar.gz](http://www.charliemouse.com:8080/code/cambozola/cambozola-latest.tar.gz) tar xvfz cambozola-latest.tar.gz sudo cp cambozola-*/dist/cambozola.jar /var/www/zm/ Make Zoneminder the root-page in Apache: sudo joe /etc/apache2/sites-enabled/000-default Change “DocumentRoot /var/www” to “DocumentRoot /var/www/zm” and “Directory /var/www/” to “Directory /var/www/zm/” Restart Apache: sudo /etc/init.d/apache2 restart Change some system parameters: sudo sysctl kernel.shmall=134217728 sudo sysctl kernel.shmmax=134217728 Make the system parameters permanent: sudo joe /etc/sysctl.conf Add the following lines at the end: kernel.shmall=134217728 kernel.shmmax=134217728 Install the startup-script (from the [official site](http://www.zoneminder.com/wiki/index.php/Debian_init.d)): sudo wget [https://vpetersson.com/upload/zm](https://vpetersson.com/upload/zm) -O /etc/init.d/zm sudo chmod +x /etc/init.d/zm sudo update-rc.d zm defaults sudo /etc/init.d/zm start That’s it. You should now have a fully working version of ZoneMinder. All you need to do now is to point your browser to the IP address of the server. **Update**: Thanks to Peter for pointing out that that there is a newer version of Cambozola. The guide has been updated to reflect this. --- # Trouble with ZoneMinder VA 0.2 and VMware Server 2 URL: https://vpetersson.com/2010/05/17/trouble-with-zoneminder-va-0-2-and-vmware-server-2/ Date: 2010-05-17 Last week I launched [ZoneMinder VA 0.2](https://vpetersson.com/open-source/zoneminder-virtual-appliance/). Unfortunately there is an issue with the image that prevents it from loading properly into VMware Server 2. The root of the problem is actually an incompatibility issue between VMware Fusion and VMware Server, but that doesn’t matter. Fortunately the workaround is pretty simple. - Delete any vmdk.lck directries - Delete any vmem.lck directories - Delete the quicklook-cache.png file - Edit the .vmx file and set to FALSE entries for USB, SOUND and SERIAL. --- # Red iGone screencast/demo URL: https://vpetersson.com/2010/05/15/red-igone-screencastdemo/ Date: 2010-05-15 I just recorded a screencast of [Red iGone](http://www.redigone.com) in action. The video is a bit rough (in particular the audio), but I hope it gets the message across. Enjoy.

--- # Hello Grub, you suck! URL: https://vpetersson.com/2010/05/14/hello-grub-you-suck/ Date: 2010-05-14 In the last few weeks I had to set up a few new Linux servers. Since Ubuntu is my preferred Linux dist in recent years, 10.04 LTS was a natural choice. Ubuntu 10.04 LTS is a great Linux distribution, with one exception: Grub. I really mean it. Grub is probably the worst boot loader to date. Is so bad that it could equally well be replaced with the following shell script: echo "" sleep 10 The better alternative to Grub is obviously Lilo. While it may be obsolete, poorly updated and 20 years old, it does one thing that Grub doesn’t: it works. Personally I couldn’t care less about fancy splash screens and all the bells and whistles that Grub can put on it’s repertoire. It’ doesn’t matter when it cannot boot the system. Please Ubuntu, just ditch Grub and make Lilo the default boot loader. --- # ZoneMinder Virtual Appliance 0.2 Released URL: https://vpetersson.com/2010/05/09/zoneminder-virtual-appliance-0-2-released/ Date: 2010-05-09 ZoneMinder is a great piece software. It is a very powerful video surveillance tool that can be configured with both IP cameras and regular cameras (via V4L). Unfortunately it is a bit difficult to get up and running with. A while back I needed to deploy ZoneMinder myself for a client. One thing lead to another, and I ended up with a fully working Virtual Machine for ZoneMinder. I uploaded it here just for fun, but it didn’t take long before I had was linked to from the official ZoneMinder project and the visitors started to pour in. Today I just released version 0.2. It is using the latest LTS version of Ubuntu (10.04) and ZoneMinder 1.24.2. I’ve also reduced the size of the VM significantly. The new version is about 1GB extracted and around 350MB compressed. For more information about the VM and how to download it, please visit this page. --- # We've taken over the management of Google Community.com URL: https://vpetersson.com/2010/04/30/weve-taken-over-the-management-of-google-community-com/ Date: 2010-04-30 A while back, WireLoad took over the management for the forum [Google Community.com](http://www.googlecommunity.com). I've been a member of the forum for a few years, and seen how it has gone from a lively forum to a spam-infested forum with hardly any active members. As the spam increased, all the serious and senior members went away. I knew that the forum had potential. It used to generate a massive amount of traffic and has more than 40,000 registered members. We actually used to receive a significant amount of traffic from Google Community to YippieMove, but as the traffic dropped, so did the traffic coming from Google Community over to YippieMove. As you can see in the graph below, the traffic was on a pretty sharp decline: ![Google Community traffic - from Compete](http://grapher.compete.com/googlecommunity.com_uv_460.png "Google Community traffic - from Compete") When I reached out to Elliot, the founder of Google Community, to ask if we could take over the maintenance for the forum. Elliot took us up on the offer, saying that he was too busy with his recent ventures to be able to manage the Google Community. After spending a significant amount of time trying to understand the setup and the software (vBulletin) we started the long process of bringing Google Community back to the lively community it once was. Two days ago, we upgraded to vBulletin 4, the latest version of the forum software. It is a major step in the right direction, and we're really happy to see many of the old members returning. It's been quite the journey to turn Google Community around, and we've pissed off a lot of script-kiddies and spammers along the way, who have used the forum to post spam. Just days after we took over the management of the forum, the site got hacked using one of the numerous bugs in vBulletin. Then, just hours after we got the site up, it got hacked again, using another vulnerability, and then again. In less than two weeks, the site was hacked three times. Hopefully that is over now, as we are running on a clean installation and with the latest version of vBulletin. Another lesson we've learned is that vBulletin is probably the worst piece of software on the on the entire internet. Unfortunately, due to lack of competition, they can keep making a fortune from producing garbage software. How else would a company get away with **requiring users to put a link-back to the vendor on a their site for a software that they purchased**. And the plug-in, vbSEO is not a bit better, as they apply the same shady business practice with back-links. **I urge the internet community to create a better forum software than vBulletin**. I mean, it cannot be that hard. The bar is really not that high. All you need to do is to **not** use web tools from the early 90's and a tad of common sense when it comes to usability, and you're already at a head start. If WireLoad had the time and resources to create a forum software, we would. On a positive note, we did get to know the fine people over at [Stackable](http://www.stackable.com), where we decided to host Google Community. So far our contact there, Mike, has been very helpful and quick on resolving all issues we've had. --- # Year 2010: Nokia relaunches Danger's Sidekick 2 URL: https://vpetersson.com/2010/04/27/year-2010-nokia-relaunches-dangers-sidekick-2/ Date: 2010-04-27 The hat is off to Nokia. They’ve managed to stay inside their cave in Finland while the rest of the world evolved. First they tried to sue Apple over some vague copyright infringement, while we all know that it was really just a desperate attempt to get some press. While Nokia once was an innovative cellphone manufacturer, I really don’t know what went wrong. Today Nokia did something remarkable though: They re-launched Danger’s Sidekick 2 under the name N8. Take a look at the similarities after the jump. Well done Nokia. The Sidekick 2 was released in 2002. Eight years later you release the same product. Astonishing. \[flickr\]http://www.flickr.com/photos/48423662@N07/4557109949/\[/flickr\] --- # Just launched Red iGone URL: https://vpetersson.com/2010/04/21/just-launched-red-igone/ Date: 2010-04-21 Two days ago we launched [Red iGone](http://www.redigone.com) — the easiest way to remove red-eyes out of you photos. It’s a dead-simple tool. Just upload your photo, select the red eye, let Red iGone work its magic, and download the enhanced photo. It’s fully web-based, and does not require anything else than your web browser. While it is still in beta, it works pretty well (with occasional hick-ups). Only hours after launching, we had received 20 some tweets, a few hundred unique visitors and two blog-reviews, so I think it is looking pretty promising. Red iGone is the first product from a new company that I founded with [Stefan Blomqwist](http://www.blomqwist.com/) called Devify. **Update**: 48 hours after launch, we’ve seen numerous blog posts about Red iGone and over 100 tweets. Not too shabby. --- # I'm going VoIP - Unboxing Snom M3. URL: https://vpetersson.com/2010/03/16/im-going-voip-unboxing-snom-m3/ Date: 2010-03-16 For many years I’ve been excited about VoIP. I attended a seminar on Asterisk 7 years or so ago, and remember thinking: Wow, this is the future. Unfortunately there were many things holding VoIP back then (bandwidth being the most obvious one). However, today most companies can get a decent internet connection (10Mbit up and down will do). Moreover, you won’t even have to get dirty with setting up your own Asterisk server today, you can simply go for a hosted PBX-solution. This is both cheaper (assuming you can’t do it yourself) and probably more reliable. This is exactly what I’m going for. After playing around with [Trixbox](http://www.trixbox.org) (a turn-key Asterisk appliance), I realized that the burden of managing the system myself (setting up HA, maintaining the software etc.) exceeds the benefit. Hence I decided to go for a hosted solution. The beauty with going for a hosted solution is that you just need to plug in the IP phones into the network, and you’re good to go. It’s almost as easy as plugging in a traditional PSTN phone. (The only exception is that you need to configure the credentials to the PBX). After speaking with a few people who knows VoIP well, I decided to go with [Snom](http://www.snom.com)-phones. They’re both cheap and offers great features. Moreover, the Snom M3 is DECT phone (it uses DECT to communicate with the base station). This means that you get much better battery-life than a WiFi phone. I ordered two Snom M3 (one kit and one extra handset) as well as a repeater to try out before I ordered more. So far I’m a happy camper. The Snom M3 reception is so great that I don’t even need the repeater, even though the office has really thick walls and is two stories. The only trouble I’ve encountered this far is when I do SIP to SIP calls. Apparently, there is a bug in [pfSense](http://www.pfsense.org) (the router software) that interferes. Yet, so far I’m happy with the result. As soon as I’ve resolved the problem with pfSense, I’m ready to move on and test the workflow for the incoming calls that I’ve created. ### Unboxing photos \[flickr-gallery mode=“photoset” photoset=“72157623508061475”\] --- # Brilliant set of SEO bookmarks URL: https://vpetersson.com/2010/02/09/brilliant-set-of-seo-bookmarks/ Date: 2010-02-09 SEO legend Rand Fishkin just published a [brilliant post](http://www.seomoz.org/blog/30-seo-bookmarklets-to-save-you-time) over at SEOMoz. It’s simple and beautiful. It’s a set of 30 bookmarks for automating bits and pieces of SEO and SERPS analysis. Kudos to Rand for putting it out there. --- # Want to learn about SEO? URL: https://vpetersson.com/2010/02/04/want-to-learn-about-seo/ Date: 2010-02-04 If you’re new to Search Engine Optimization, or SEO, you might want to take a look at [this](http://www.google.com/webmasters/docs/search-engine-optimization-starter-guide.pdf) brief e-book from Google. It goes over the most basic aspects of SEO. That said, even if you consider yourself a savvy-webmaster, it could still be worth your time just in case you missed something --- # New email troubleshooting guide URL: https://vpetersson.com/2010/02/04/new-email-troubleshooting-guide/ Date: 2010-02-04 I just published a guide on how to troubleshoot email over at [Email Service Guide](http://www.emailserviceguide.com/email-troubleshooting-guide/). It’s fairly straight forward, but does require a bit of technical skills. The reason why I wrote the guide was actually because I quite frequently found myself Googling for a good IMAP cheat sheet (the RFC is not very good for this). Once I started writing, I realized that it wouldn’t take a whole lot more effort to include POP3 and SMTP as well (since the technique is the same). So I included them too. --- # Two new partners for YippieMove URL: https://vpetersson.com/2010/02/03/two-new-partners-for-yippiemove/ Date: 2010-02-03 Today we just introduced two new partners for YippieMove: LTech and Weird Kid Software. They’re both leaders in their particular niche and we’re excited to start working with them. For more information, see the [partner-page](http://www.yippiemove.com/partners.html) at YippieMove.com.