<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Adam Gradzki's Personal Website</title><link href="https://adamgradzki.com/" rel="alternate"/><link href="https://adamgradzki.com/feeds/all.atom.xml" rel="self"/><id>https://adamgradzki.com/</id><updated>2025-11-07T00:00:00-05:00</updated><entry><title>Adding DeepFilterNet Noise Reduction to Easy Effects on Arch Linux</title><link href="https://adamgradzki.com/adding-deepfilternet-noise-reduction-to-easy-effects-on-arch-linux.html" rel="alternate"/><published>2025-11-07T00:00:00-05:00</published><updated>2025-11-07T00:00:00-05:00</updated><author><name>Adam Gradzki</name></author><id>tag:adamgradzki.com,2025-11-07:/adding-deepfilternet-noise-reduction-to-easy-effects-on-arch-linux.html</id><summary type="html">&lt;p&gt;How to install and configure DeepFilterNet neural network noise reduction for Easy Effects when the LADSPA plugin is missing from your Linux distribution's package manager.&lt;/p&gt;</summary><content type="html">&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Easy Effects (formerly PulseEffects) is a powerful audio equalizer and effects system for Linux that provides studio-quality audio processing right from your desktop. While the basic installation through most Linux distributions includes a comprehensive suite of audio effects, one crucial component is often missing: the neural network-based noise reduction plugin from DeepFilterNet.&lt;/p&gt;
&lt;p&gt;DeepFilterNet is a state-of-the-art speech enhancement framework that uses deep learning to remove background noise from audio in real-time. Unlike traditional noise gates or spectral subtraction methods, DeepFilterNet leverages advanced neural networks trained on thousands of hours of audio to distinguish between speech and background noise with remarkable accuracy. This makes it particularly effective for removing complex, dynamic background sounds like keyboard typing, fan noise, or ambient chatter while preserving the clarity and natural quality of human speech.&lt;/p&gt;
&lt;p&gt;Unfortunately, many Linux distributions, including Arch Linux, don't package the DeepFilterNet LADSPA plugin alongside Easy Effects due to licensing restrictions, file size considerations, or simply packaging oversights. This leaves users with a powerful audio processing suite but missing one of its most impressive capabilities.&lt;/p&gt;
&lt;p&gt;In this guide, we'll walk through the process of manually installing the DeepFilterNet plugin to complete your Easy Effects setup, enabling professional-grade noise reduction for podcasts, voice calls, recordings, and any other audio application where clean speech matters.&lt;/p&gt;
&lt;h2 id="understanding-the-components"&gt;Understanding the Components&lt;/h2&gt;
&lt;p&gt;Before diving into the installation process, it's helpful to understand what we're working with:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Easy Effects&lt;/strong&gt; provides the framework and user interface for audio processing. It acts as a host for various LADSPA (Linux Audio Developer's Simple Plugin API) plugins, managing the audio routing and providing an intuitive GUI for controlling the effects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DeepFilterNet&lt;/strong&gt; is the neural network engine that performs the actual noise reduction. It processes audio in real-time, analyzing the frequency spectrum and using machine learning models to identify and suppress background noise while preserving speech components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LADSPA Plugin&lt;/strong&gt; serves as the bridge between Easy Effects and DeepFilterNet. This plugin follows the LADSPA standard, allowing Easy Effects to load and control the DeepFilterNet processing engine as if it were any other audio effect.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before proceeding with the installation, ensure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Easy Effects installed&lt;/strong&gt;: Available through your distribution's package manager&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Basic command-line knowledge&lt;/strong&gt;: Comfortable with terminal commands and file operations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sudo/root access&lt;/strong&gt;: Required for system-level file operations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internet connection&lt;/strong&gt;: To download the DeepFilterNet plugin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On Arch Linux, you can install Easy Effects using:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo pacman -S easyeffects
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="the-installation-process"&gt;The Installation Process&lt;/h2&gt;
&lt;h3 id="step-1-identify-your-system-architecture"&gt;Step 1: Identify Your System Architecture&lt;/h3&gt;
&lt;p&gt;First, determine whether you're running a 64-bit system (most common) or need a different architecture. You can check this using:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;uname -m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For most modern systems, this will return &lt;code&gt;x86_64&lt;/code&gt;, indicating a 64-bit architecture.&lt;/p&gt;
&lt;h3 id="step-2-download-the-deepfilternet-plugin"&gt;Step 2: Download the DeepFilterNet Plugin&lt;/h3&gt;
&lt;p&gt;The DeepFilterNet project provides pre-compiled LADSPA plugins through their GitHub releases. Visit the official releases page to find the appropriate version for your system:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub Releases URL&lt;/strong&gt;: https://github.com/Rikorose/DeepFilterNet/releases&lt;/p&gt;
&lt;p&gt;Look for the latest stable release and download the appropriate LADSPA plugin file. For 64-bit systems, you'll typically want a file named something like:
- &lt;code&gt;libdeep_filter_ladspa-0.5.6-x86_64-unknown-linux-gnu.so&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can download this file directly using &lt;code&gt;wget&lt;/code&gt; or &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example downloading version 0.5.6 for x86_64&lt;/span&gt;
&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;LO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Rikorose&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;DeepFilterNet&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;releases&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;5.6&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;libdeep_filter_ladspa&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gnu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;so&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, you can use your web browser to download the file from the releases page.&lt;/p&gt;
&lt;h3 id="step-3-install-the-plugin-to-the-system-ladspa-directory"&gt;Step 3: Install the Plugin to the System LADSPA Directory&lt;/h3&gt;
&lt;p&gt;Linux audio applications typically look for LADSPA plugins in standard system directories. The most common location is &lt;code&gt;/usr/lib64/ladspa/&lt;/code&gt; on 64-bit systems. We'll move the downloaded plugin to this location and give it a simpler name that Easy Effects can easily recognize.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;#&lt;/span&gt; Move the plugin to the system LADSPA directory and rename it
sudo mv -v libdeep_filter_ladspa-0.5.6-x86_64-unknown-linux-gnu.so /usr/lib64/ladspa/libdeep_filter_ladspa.so
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-v&lt;/code&gt; flag provides verbose output, confirming the move operation. The command should output something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;renamed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/path/to/downloaded/libdeep_filter_ladspa-0.5.6-x86_64-unknown-linux-gnu.so&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/usr/lib64/ladspa/libdeep_filter_ladspa.so&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why rename the file?&lt;/strong&gt; The version-specific naming in the downloaded file helps with package management, but Easy Effects typically looks for plugins with simpler, standardized names. Renaming to &lt;code&gt;libdeep_filter_ladspa.so&lt;/code&gt; makes it easier for the application to discover and load the plugin.&lt;/p&gt;
&lt;h3 id="step-4-verify-the-installation"&gt;Step 4: Verify the Installation&lt;/h3&gt;
&lt;p&gt;After installing the plugin, you can verify that it's properly placed and accessible:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;#&lt;/span&gt; Check that the file exists and has appropriate permissions
ls -la /usr/lib64/ladspa/libdeep_filter_ladspa.so
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This should show the plugin file with read permissions for all users and appropriate ownership (typically root:root).&lt;/p&gt;
&lt;h3 id="step-5-configure-easy-effects"&gt;Step 5: Configure Easy Effects&lt;/h3&gt;
&lt;p&gt;Now that the plugin is installed, it's time to configure it in Easy Effects:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Launch Easy Effects&lt;/strong&gt;: Start the application from your application menu or using the command line:
   &lt;code&gt;easyeffects&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Select Input Device&lt;/strong&gt;: In the Easy Effects window, make sure you're working with the correct input device (your microphone).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add DeepFilterNet Plugin&lt;/strong&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Click on the "Add effect" or "+" button in the input effects section&lt;/li&gt;
&lt;li&gt;Look for "DeepFilterNet" in the list of available plugins&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select it to add to your effects chain&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure the Plugin&lt;/strong&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Noise Reduction Level&lt;/strong&gt;: Start with a moderate setting (around 50-70%)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aggressiveness&lt;/strong&gt;: Higher settings remove more noise but may affect speech quality&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Voice Preservation&lt;/strong&gt;: Balance between noise removal and natural speech quality&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test the Configuration&lt;/strong&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Speak into your microphone while playing some background noise&lt;/li&gt;
&lt;li&gt;Adjust the settings in real-time to find the optimal balance&lt;/li&gt;
&lt;li&gt;Use the bypass toggle to compare the processed and unprocessed audio&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="important-configuration-steps"&gt;Important Configuration Steps&lt;/h2&gt;
&lt;h3 id="configure-easy-effects-to-launch-on-startup"&gt;Configure Easy Effects to Launch on Startup&lt;/h3&gt;
&lt;p&gt;To ensure your noise reduction settings are persistent and always active when you need them, you should configure Easy Effects to launch automatically when you log in:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option 1: Use Easy Effects Built-in Startup Setting (Recommended)&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Easy Effects&lt;/li&gt;
&lt;li&gt;Click the menu button (☰) in the top-right corner&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Preferences&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;General&lt;/strong&gt; tab, look for &lt;strong&gt;"Launch Easy Effects at startup"&lt;/strong&gt; or &lt;strong&gt;"Start on login"&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Enable this toggle option&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Option 2: System-level Startup Configuration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you prefer system-level configuration or the built-in option isn't available:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For systems with GNOME Desktop:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Applications&lt;/strong&gt; → &lt;strong&gt;Startup Applications&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add&lt;/strong&gt; and browse to Easy Effects&lt;/li&gt;
&lt;li&gt;Alternatively, run this command to add it to startup:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp /usr/share/applications/com.github.wwmm.easyeffects.desktop ~/.config/autostart/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;For systems with KDE Plasma:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;System Settings&lt;/strong&gt; → &lt;strong&gt;Startup and Shutdown&lt;/strong&gt; → &lt;strong&gt;Autostart&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add Application&lt;/strong&gt; and select Easy Effects&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Why is this important?&lt;/strong&gt; Easy Effects needs to be running to process audio through the DeepFilterNet plugin. Without it configured to launch on startup, you'll need to manually start the application each time you log in, and your noise reduction settings won't be automatically applied.&lt;/p&gt;
&lt;h3 id="configure-applications-to-use-the-correct-audio-input"&gt;Configure Applications to Use the Correct Audio Input&lt;/h3&gt;
&lt;p&gt;This is a crucial step that many users miss: &lt;strong&gt;applications must use the "Easy Effects Source" virtual input device, not your standard microphone input.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When Easy Effects is running, it creates a virtual input source called &lt;strong&gt;"Easy Effects Source"&lt;/strong&gt; (or similar depending on your system configuration). This virtual device captures your raw microphone input, processes it through DeepFilterNet and any other effects you've configured, and then outputs the filtered audio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to configure your applications:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For audio recording software (Audacity, etc.):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to recording preferences&lt;/li&gt;
&lt;li&gt;Select "Easy Effects Source" or "Monitor of Easy Effects" as the input device&lt;/li&gt;
&lt;li&gt;Do NOT select your physical microphone directly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;For video conferencing applications:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zoom&lt;/strong&gt;: In Settings → Audio, select "Easy Effects Source" as the microphone&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discord&lt;/strong&gt;: In Voice &amp;amp; Video settings, select "Easy Effects Source" as the input device&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teams/Google Meet&lt;/strong&gt;: Check application settings to select the virtual input device&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;For system-wide configuration:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In your desktop environment's sound settings, you can often set the default input device to "Easy Effects Source"&lt;/li&gt;
&lt;li&gt;This ensures all applications automatically use the filtered audio&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Important Warning&lt;/strong&gt;: If applications use your standard microphone input instead of "Easy Effects Source," they will bypass all audio processing entirely. This means DeepFilterNet noise reduction (and any other effects) will not be applied, and you'll wonder why the noise reduction isn't working despite everything being correctly configured in Easy Effects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To verify it's working:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start Easy Effects with DeepFilterNet enabled&lt;/li&gt;
&lt;li&gt;Open your system sound settings or pavucontrol&lt;/li&gt;
&lt;li&gt;Look at the input devices - you should see both your physical microphone and "Easy Effects Source"&lt;/li&gt;
&lt;li&gt;Configure your recording/conferencing app to use "Easy Effects Source"&lt;/li&gt;
&lt;li&gt;Test by recording audio or joining a call - the noise reduction should now be active&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="troubleshooting-common-issues"&gt;Troubleshooting Common Issues&lt;/h2&gt;
&lt;h3 id="plugin-not-appearing-in-easy-effects"&gt;Plugin Not Appearing in Easy Effects&lt;/h3&gt;
&lt;p&gt;If DeepFilterNet doesn't appear in your list of available plugins:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Restart Easy Effects&lt;/strong&gt;: Completely close and restart the application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check file permissions&lt;/strong&gt;: Ensure the plugin is readable by all users:
   &lt;code&gt;sudo chmod 644 /usr/lib64/ladspa/libdeep_filter_ladspa.so&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify LADSPA path&lt;/strong&gt;: Some systems may use different paths. Check if plugins are loaded from:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/usr/lib/ladspa/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/usr/local/lib/ladspa/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/.ladspa/&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="audio-quality-issues"&gt;Audio Quality Issues&lt;/h3&gt;
&lt;p&gt;If you experience poor audio quality or distortion:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Reduce noise reduction intensity&lt;/strong&gt;: Lower the settings to preserve more natural speech&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check input levels&lt;/strong&gt;: Ensure your microphone isn't clipping or too quiet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Update audio drivers&lt;/strong&gt;: Outdated audio drivers can cause compatibility issues&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="performance-issues"&gt;Performance Issues&lt;/h3&gt;
&lt;p&gt;If you experience high CPU usage or audio stuttering:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Close other applications&lt;/strong&gt;: Neural network processing can be CPU-intensive&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adjust buffer sizes&lt;/strong&gt;: In Easy Effects settings, increase the buffer size if available&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check system resources&lt;/strong&gt;: Monitor CPU usage during processing&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="advanced-configuration"&gt;Advanced Configuration&lt;/h2&gt;
&lt;p&gt;For users wanting more control over the noise reduction process:&lt;/p&gt;
&lt;h3 id="multiple-microphone-setup"&gt;Multiple Microphone Setup&lt;/h3&gt;
&lt;p&gt;If you use multiple microphones, you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure separate instances of DeepFilterNet for each input&lt;/li&gt;
&lt;li&gt;Use different settings depending on the microphone's characteristics and use case&lt;/li&gt;
&lt;li&gt;Create preset configurations for different scenarios (podcasting, voice calls, recording)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="integration-with-other-effects"&gt;Integration with Other Effects&lt;/h3&gt;
&lt;p&gt;DeepFilterNet works well in combination with other audio effects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Compressor&lt;/strong&gt;: Apply after noise reduction to even out volume levels&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Equalizer&lt;/strong&gt;: Fine-tune the frequency response after noise processing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limiter&lt;/strong&gt;: Prevent audio clipping after processing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="automation-and-scripts"&gt;Automation and Scripts&lt;/h3&gt;
&lt;p&gt;For advanced users, you can automate the noise reduction setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use command-line tools to enable/disable the effect&lt;/li&gt;
&lt;li&gt;Create scripts that switch between different noise reduction profiles&lt;/li&gt;
&lt;li&gt;Integrate with recording software for automatic noise reduction&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="performance-considerations"&gt;Performance Considerations&lt;/h2&gt;
&lt;p&gt;DeepFilterNet represents a significant advancement in noise reduction technology, but it's important to understand its resource requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CPU Usage&lt;/strong&gt;: The neural network processing requires more CPU power than traditional noise reduction methods. On modern systems, this is typically not an issue, but older hardware may struggle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latency&lt;/strong&gt;: There's minimal processing delay, but it's slightly higher than simpler noise gates. For most applications (voice calls, recording), this is imperceptible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory Usage&lt;/strong&gt;: The neural network models require some RAM to load, but the overhead is reasonable for the quality improvement provided.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="alternative-solutions"&gt;Alternative Solutions&lt;/h2&gt;
&lt;p&gt;While DeepFilterNet is excellent, it's worth knowing about other noise reduction options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Traditional Noise Gates&lt;/strong&gt;: Simpler, less resource-intensive but less effective for complex noise&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spectral Subtraction&lt;/strong&gt;: Good for constant background noise but can create artifacts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commercial Solutions&lt;/strong&gt;: Professional audio software with advanced noise reduction capabilities&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Installing DeepFilterNet for Easy Effects transforms your Linux audio setup from good to professional-grade. The neural network-based noise reduction can dramatically improve audio quality for voice recordings, podcasts, video calls, and any application where clear speech communication is essential.&lt;/p&gt;
&lt;p&gt;While the manual installation process might seem intimidating at first, it's actually quite straightforward and provides access to one of the most advanced noise reduction technologies available in open-source audio processing. The ability to remove complex background noise while preserving natural speech quality makes this setup particularly valuable for content creators, remote workers, and anyone who needs clear audio in less-than-ideal environments.&lt;/p&gt;
&lt;p&gt;Take the time to experiment with different settings and find the configuration that works best for your specific use case and environment. The results can be truly impressive, turning noisy, unusable audio into clear, professional-quality sound.&lt;/p&gt;
&lt;h2 id="further-reading"&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/wwmm/easyeffects/wiki"&gt;Easy Effects Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Rikorose/DeepFilterNet"&gt;DeepFilterNet GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ladspa.org/"&gt;LADSPA Plugin Standard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.linuxaudio.org/wiki/start"&gt;Linux Audio Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Software"/><category term="linux"/><category term="arch-linux"/><category term="audio"/><category term="noise-reduction"/><category term="easy-effects"/><category term="deepfilternet"/><category term="sound"/></entry><entry><title>Integrating JIRA with Claude Code: A Complete Setup Guide</title><link href="https://adamgradzki.com/jira-claude-code-integration.html" rel="alternate"/><published>2025-10-03T16:00:00-04:00</published><updated>2025-10-03T16:00:00-04:00</updated><author><name>Adam Gradzki</name></author><id>tag:adamgradzki.com,2025-10-03:/jira-claude-code-integration.html</id><summary type="html">&lt;p&gt;Learn how to integrate JIRA with Claude Code using the MCP Atlassian server for seamless project management directly from your terminal.&lt;/p&gt;</summary><content type="html">&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Managing JIRA tickets while coding can be a context-switching nightmare. Constantly jumping between your IDE, terminal, and browser disrupts your flow and wastes valuable development time. Enter the MCP Atlassian integration for Claude Code, a powerful solution that brings JIRA functionality directly into your command-line workflow.&lt;/p&gt;
&lt;p&gt;This guide will walk you through setting up the MCP (Model Context Protocol) Atlassian server with Claude Code, enabling you to interact with JIRA issues, update tickets, and manage your workflow without leaving your terminal.&lt;/p&gt;
&lt;h2 id="how-to-use-mcp-tools-in-claude"&gt;How to Use MCP Tools in Claude&lt;/h2&gt;
&lt;h3 id="a-natural-conversation-approach"&gt;A Natural Conversation Approach&lt;/h3&gt;
&lt;p&gt;You've just discovered that Claude has access to powerful integrations through the Model Context Protocol (MCP). Maybe you opened the MCP dialog and saw a list of 42 Jira tools staring back at you. You tried typing a slash command. It didn't work. You're wondering: "How am I supposed to use this tool?"&lt;/p&gt;
&lt;p&gt;If this sounds familiar, you're not alone. The interface can be deceiving, making it seem like you need special commands or technical syntax to access these capabilities. But here's the truth that will change how you work with Claude: &lt;strong&gt;you don't need commands at all&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="what-are-mcp-tools"&gt;What Are MCP Tools?&lt;/h3&gt;
&lt;p&gt;Before we dive into the "how," let's quickly understand the "what." MCP (Model Context Protocol) tools are integrations that give Claude direct access to your external systems and services. In the example we're discussing, we're looking at Jira integration, which includes tools like getting user profiles, searching for issues, retrieving project details, and much more.&lt;/p&gt;
&lt;p&gt;These aren't just theoretical capabilities. When properly configured, Claude can actually query your Jira instance, pull real data, and help you manage your work without you ever leaving the conversation.&lt;/p&gt;
&lt;h3 id="the-paradigm-shift-natural-language-is-the-interface"&gt;The Paradigm Shift: Natural Language Is the Interface&lt;/h3&gt;
&lt;p&gt;Here's the revolutionary part that many users miss: &lt;strong&gt;the interface is conversation itself&lt;/strong&gt;. You don't interact with these tools through commands, menus, or special syntax. You simply talk to Claude the way you would talk to a knowledgeable colleague who has access to your systems.&lt;/p&gt;
&lt;p&gt;When you see that MCP dialog showing dozens of available tools, it's not a menu you need to navigate. It's simply showing you what's possible behind the scenes. Think of it like seeing the engine compartment of a car. You're glad to know what's under the hood, but you don't need to touch any of those components directly to drive.&lt;/p&gt;
&lt;h3 id="how-it-actually-works"&gt;How It Actually Works&lt;/h3&gt;
&lt;p&gt;Let me walk you through the magic of how this actually functions in practice. When you type a message to Claude, it analyzes what you're asking for. If your request involves information that it can retrieve using an available tool, it automatically calls that tool, get the results, and incorporate them into the response.&lt;/p&gt;
&lt;p&gt;You might say something as simple as "Show me my Jira profile." Behind the scenes, it recognize that you're asking for Jira user profile information. It automatically invoke the jira_get_user_profile tool, retrieve your data, and present it to you in a readable format. You never see the tool being called. You just see the answer to your question.&lt;/p&gt;
&lt;p&gt;The same principle applies to more complex requests. If you ask "Find all high-priority bugs assigned to me in the Q4 project," it understands that this requires searching Jira with specific filters. I'll automatically use the appropriate search tool with the right parameters, even though you never specified which tool to use or how to structure the query.&lt;/p&gt;
&lt;h3 id="practical-examples-from-questions-to-results"&gt;Practical Examples: From Questions to Results&lt;/h3&gt;
&lt;p&gt;Let's look at some real-world examples of how you might interact with these tools through natural conversation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Getting Your Profile Information&lt;/strong&gt;: You could simply say "What's in my Jira profile?" or "Show me my Jira user info." I'll fetch your profile details and present them in a clear, readable way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finding Specific Issues&lt;/strong&gt;: Try asking "Get details about issue ABC-123" or "What's the status of ticket XYZ-456?" I'll retrieve the complete issue information including description, status, assignee, comments, and other relevant details.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Searching Across Projects&lt;/strong&gt;: You might ask "Search for bugs in the Mobile App project" or "Find all open issues assigned to me." I'll construct the appropriate search query and return the results in a format that's easy to scan and understand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exploring Project Contents&lt;/strong&gt;: Questions like "What issues are in the Engineering project?" or "Show me all the stories in our current sprint" will trigger the right tools to fetch and display that project-specific information.&lt;/p&gt;
&lt;p&gt;The key insight is that none of these examples require you to know which specific tool does what. You're just asking for what you need in plain English.&lt;/p&gt;
&lt;h3 id="what-about-those-slash-commands"&gt;What About Those Slash Commands?&lt;/h3&gt;
&lt;p&gt;You might be wondering about the slash commands you tried to use. In Claude's interface, slash commands do exist for certain built-in features, but they're not how you access MCP tools. The slash commands are for interface-level actions, not for invoking external integrations.&lt;/p&gt;
&lt;p&gt;When you type something like "/jira" and get an "Unknown slash command" error, that's the interface telling you that this isn't the right approach. The MCP tools aren't activated through slash commands because they're designed to be more intuitive than that.&lt;/p&gt;
&lt;h3 id="the-technical-magic-you-dont-need-to-understand"&gt;The Technical Magic You Don't Need to Understand&lt;/h3&gt;
&lt;p&gt;Here's what's happening behind the curtain, in case you're curious. When MCP tools are configured for your Claude instance, they register themselves as available capabilities. Each tool comes with a description of what it does and what parameters it accepts. When you send it a message, it evaluate whether any of the available tools can help answer your question. If they can, it automatically formulates the appropriate tool call, execute it, receives the results, and incorporate them into the response.&lt;/p&gt;
&lt;p&gt;This happens in milliseconds, and from your perspective, it just looks like I'm giving you well-informed answers. You don't need to understand this process to benefit from it. That's the whole point.&lt;/p&gt;
&lt;h3 id="best-practices-for-getting-the-most-value"&gt;Best Practices for Getting the Most Value&lt;/h3&gt;
&lt;p&gt;While you don't need special syntax, there are some conversational approaches that work particularly well with MCP tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Be specific when it matters&lt;/strong&gt;. If you're looking for a particular issue, include its key identifier. If you're searching for items with specific criteria, mention those filters. The more context you provide, the better it can target tool usage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ask follow-up questions naturally&lt;/strong&gt;. Once I've retrieved some information, you can dig deeper with questions like "Tell me more about that first issue" or "What are the comments on that bug?" it maintains context from our conversation and can make subsequent tool calls as needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don't worry about tool names&lt;/strong&gt;. You never need to say "Use the jira_search tool to find..." Just say "Find issues that..." and let it handle the technical implementation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Combine requests freely&lt;/strong&gt;. You can ask complex questions that might require multiple tool calls, like "Compare the number of open issues across all our active projects." I'll orchestrate whatever tools are needed to get you a complete answer.&lt;/p&gt;
&lt;h3 id="when-things-dont-work-as-expected"&gt;When Things Don't Work as Expected&lt;/h3&gt;
&lt;p&gt;Sometimes you might ask for something and not get the results you hoped for. This usually isn't about using the wrong syntax since there's no special syntax required. Instead, it might be that the tools aren't configured yet, you don't have the right permissions in the external system, or the tool doesn't support that specific operation.&lt;/p&gt;
&lt;p&gt;If it can't fulfill a request, I'll let you know why. Maybe the Jira integration isn't set up yet, or perhaps your Jira permissions don't allow access to certain projects. The conversation itself becomes the debugging interface.&lt;/p&gt;
&lt;h3 id="the-broader-implication"&gt;The Broader Implication&lt;/h3&gt;
&lt;p&gt;What we're really talking about here extends beyond just Jira tools. The Model Context Protocol can connect Claude to all sorts of systems: databases, project management tools, customer relationship platforms, code repositories, and more. The principle remains the same across all of them: natural language is your interface.&lt;/p&gt;
&lt;p&gt;This represents a fundamental shift in how we interact with software. Instead of learning each tool's specific command syntax, navigation structure, or API endpoints, you simply describe what you need. The complexity is abstracted away behind conversational interaction.&lt;/p&gt;
&lt;h3 id="stop-typing-commands-start-having-conversations"&gt;Stop Typing Commands, Start Having Conversations&lt;/h3&gt;
&lt;p&gt;If you take away one thing from this guide, let it be this: stop looking for the command syntax. There isn't one. The MCP dialog showing you available tools is informative, not prescriptive. It's telling you what's possible, not dictating how you must ask for it.&lt;/p&gt;
&lt;p&gt;The next time you want to interact with your Jira instance, your database, or any other MCP-connected system through Claude, just ask your question as if you were talking to a colleague. "What issues am I working on?" "Find that bug we discussed last week." "Show me the project timeline." &lt;/p&gt;
&lt;p&gt;That's it. That's the whole secret. The future of software interaction isn't about memorizing commands; it's about having natural conversations. And with MCP tools in Claude, that future is already here.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before you begin, ensure you have the following installed and configured:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Docker&lt;/strong&gt;: The MCP Atlassian server runs as a Docker container&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;: Anthropic's command-line tool for agentic coding&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JIRA Access&lt;/strong&gt;: Valid Atlassian credentials with appropriate permissions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Basic Command-Line Knowledge&lt;/strong&gt;: Familiarity with terminal commands and environment variables&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-by-step-setup"&gt;Step-by-Step Setup&lt;/h2&gt;
&lt;h3 id="step-1-pull-the-mcp-atlassian-docker-image"&gt;Step 1: Pull the MCP Atlassian Docker Image&lt;/h3&gt;
&lt;p&gt;First, download the official MCP Atlassian Docker image from GitHub Container Registry:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker&lt;span class="w"&gt; &lt;/span&gt;pull&lt;span class="w"&gt; &lt;/span&gt;ghcr.io/sooperset/mcp-atlassian:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command fetches the latest version of the MCP Atlassian server, which acts as a bridge between Claude Code and your JIRA instance.&lt;/p&gt;
&lt;h3 id="step-2-download-the-configuration-template"&gt;Step 2: Download the Configuration Template&lt;/h3&gt;
&lt;p&gt;Retrieve the example environment configuration file and save it to your system:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-L&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://raw.githubusercontent.com/sooperset/mcp-atlassian/refs/heads/main/.env.example&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;~/.config/mcp-atlassian.env
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This creates a configuration file at &lt;code&gt;~/.config/mcp-atlassian.env&lt;/code&gt; that you'll customize with your JIRA credentials.&lt;/p&gt;
&lt;h3 id="step-3-configure-your-jira-credentials"&gt;Step 3: Configure Your JIRA Credentials&lt;/h3&gt;
&lt;p&gt;Open the configuration file in your preferred text editor:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vim&lt;span class="w"&gt; &lt;/span&gt;~/.config/mcp-atlassian.env
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You'll need to configure it based on the instructions within.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security Note&lt;/strong&gt;: Never commit this file to version control. Consider using proper secrets management for production environments.&lt;/p&gt;
&lt;h3 id="step-4-add-the-mcp-server-to-claude-code"&gt;Step 4: Add the MCP Server to Claude Code&lt;/h3&gt;
&lt;p&gt;Register the MCP Atlassian server with Claude Code using the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;claude&lt;span class="w"&gt; &lt;/span&gt;mcp&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;mcp-atlassian&lt;span class="w"&gt; &lt;/span&gt;--scope&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;--env-file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;realpath&lt;span class="w"&gt; &lt;/span&gt;~/.config/mcp-atlassian.env&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ghcr.io/sooperset/mcp-atlassian:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's break down this command:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;claude mcp add mcp-atlassian&lt;/code&gt;: Registers a new MCP server named "mcp-atlassian"&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--scope user&lt;/code&gt;: Makes this configuration available at the user level&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker run --rm -i&lt;/code&gt;: Runs the Docker container interactively and removes it when done&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--env-file $(realpath ~/.config/mcp-atlassian.env)&lt;/code&gt;: Loads your configuration file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ghcr.io/sooperset/mcp-atlassian:latest&lt;/code&gt;: Specifies the Docker image to use&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="step-5-restart-claude-code"&gt;Step 5: Restart Claude Code&lt;/h3&gt;
&lt;p&gt;For the changes to take effect, restart your Claude Code session:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Exit any active Claude Code session, then restart&lt;/span&gt;
claude
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="step-6-verify-the-connection"&gt;Step 6: Verify the Connection&lt;/h3&gt;
&lt;p&gt;Confirm that the MCP Atlassian server is properly connected by listing all active MCP servers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;claude&lt;span class="w"&gt; &lt;/span&gt;mcp&lt;span class="w"&gt; &lt;/span&gt;list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should see &lt;code&gt;mcp-atlassian&lt;/code&gt; in the list of available servers with a status indicating it's connected.&lt;/p&gt;
&lt;h3 id="step-7-test-the-integration"&gt;Step 7: Test the Integration&lt;/h3&gt;
&lt;p&gt;Within an active Claude Code session, you can verify MCP functionality by using the &lt;code&gt;/mcp&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/mcp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will display all available MCP tools and servers, including your newly configured JIRA integration.&lt;/p&gt;
&lt;h2 id="common-use-cases"&gt;Common Use Cases&lt;/h2&gt;
&lt;p&gt;Once configured, you can use Claude Code to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Query JIRA issues&lt;/strong&gt;: Search for tickets by JQL, project, or status&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create new issues&lt;/strong&gt;: Generate JIRA tickets directly from your terminal&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Update ticket status&lt;/strong&gt;: Transition issues through your workflow&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add comments&lt;/strong&gt;: Document progress without opening a browser&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View issue details&lt;/strong&gt;: Get comprehensive information about specific tickets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Link commits to issues&lt;/strong&gt;: Maintain traceability between code and tasks&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting&lt;/h2&gt;
&lt;h3 id="connection-issues"&gt;Connection Issues&lt;/h3&gt;
&lt;p&gt;If the MCP server fails to connect:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Verify your Docker daemon is running: &lt;code&gt;docker ps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check that your API token is valid and hasn't expired&lt;/li&gt;
&lt;li&gt;Ensure your JIRA URL is correct (including https://)&lt;/li&gt;
&lt;li&gt;Confirm your user account has appropriate JIRA permissions&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="configuration-not-loading"&gt;Configuration Not Loading&lt;/h3&gt;
&lt;p&gt;If environment variables aren't being recognized:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Verify the &lt;code&gt;.env&lt;/code&gt; file path is correct&lt;/li&gt;
&lt;li&gt;Check file permissions: &lt;code&gt;chmod 600 ~/.config/mcp-atlassian.env&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ensure there are no syntax errors in your configuration file&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="docker-pull-failures"&gt;Docker Pull Failures&lt;/h3&gt;
&lt;p&gt;If you can't pull the Docker image:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check your internet connection&lt;/li&gt;
&lt;li&gt;Verify you have sufficient disk space&lt;/li&gt;
&lt;li&gt;Ensure Docker has permissions to pull from GitHub Container Registry&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="best-practices"&gt;Best Practices&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Keep Your API Token Secure&lt;/strong&gt;: Treat it like a password and rotate it regularly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use Project-Specific Configuration&lt;/strong&gt;: Consider separate configurations for different projects&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage JQL&lt;/strong&gt;: Learn JIRA Query Language to make powerful searches from Claude Code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document Your Workflow&lt;/strong&gt;: Create aliases or scripts for common JIRA operations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regular Updates&lt;/strong&gt;: Periodically update the Docker image to get latest features and security patches&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="jira-confluence-tools-overview"&gt;Jira &amp;amp; Confluence Tools Overview&lt;/h2&gt;
&lt;h3 id="core-operations"&gt;Core Operations&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Jira&lt;/th&gt;
&lt;th&gt;Confluence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Search&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_search&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_search&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_search_fields&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_search_user&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Create&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_create_issue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_create_page&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_batch_create_issues&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read/Get&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_issue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_get_page&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_all_projects&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_get_page_children&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_project_issues&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Update&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_update_issue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_update_page&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delete&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_delete_issue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_delete_page&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="comments-labels"&gt;Comments &amp;amp; Labels&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Jira&lt;/th&gt;
&lt;th&gt;Confluence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Comments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_add_comment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_add_comment&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_get_comments&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Labels&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_add_label&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_get_labels&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="jira-specific-agile-sprint-management"&gt;Jira-Specific: Agile &amp;amp; Sprint Management&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Boards&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_agile_boards&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_board_issues&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sprints&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_sprints_from_board&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_sprint_issues&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_create_sprint&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_update_sprint&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="jira-specific-workflow-tracking"&gt;Jira-Specific: Workflow &amp;amp; Tracking&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transitions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_transitions&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_transition_issue&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Worklogs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_worklog&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_add_worklog&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_issue_link_types&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_create_issue_link&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_remove_issue_link&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_link_to_epic&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Versions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_project_versions&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_create_version&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_batch_create_versions&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="additional-features"&gt;Additional Features&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Jira&lt;/th&gt;
&lt;th&gt;Confluence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Profile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_get_user_profile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confluence_search_user&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attachments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_download_attachments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;History&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jira_batch_get_changelogs&lt;/code&gt;*&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;*Tool only available on Jira Cloud&lt;/p&gt;
&lt;p&gt;Integrating JIRA with Claude Code streamlines your development workflow by eliminating context switching and bringing project management capabilities directly into your terminal. With this setup complete, you can focus on coding while maintaining seamless communication with your issue tracking system.&lt;/p&gt;
&lt;p&gt;The MCP architecture opens possibilities for integrating other tools and services with Claude Code, making it a powerful hub for your entire development workflow.&lt;/p&gt;
&lt;h2 id="additional-resources"&gt;Additional Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.claude.com/en/docs/claude-code"&gt;Claude Code Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sooperset/mcp-atlassian"&gt;MCP Atlassian GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.atlassian.com/cloud/jira/platform/rest/v3/"&gt;Atlassian API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/"&gt;Model Context Protocol Specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Development Tools"/><category term="claude-code"/><category term="jira"/><category term="atlassian"/><category term="mcp"/><category term="docker"/><category term="productivity"/><category term="development-workflow"/></entry><entry><title>Lightweight Development Sandboxes with systemd-nspawn on Linux</title><link href="https://adamgradzki.com/lightweight-development-sandboxes-with-systemd-nspawn-on-linux.html" rel="alternate"/><published>2025-09-26T00:00:00-04:00</published><updated>2025-09-26T00:00:00-04:00</updated><author><name>Adam Gradzki</name></author><id>tag:adamgradzki.com,2025-09-26:/lightweight-development-sandboxes-with-systemd-nspawn-on-linux.html</id><summary type="html">&lt;p&gt;Create isolated, ultra-lightweight Linux containers for safe development environments and AI code agents using systemd-nspawn - no Docker required&lt;/p&gt;</summary><content type="html">&lt;h2 id="why-systemd-nspawn-for-development-containers"&gt;Why systemd-nspawn for Development Containers?&lt;/h2&gt;
&lt;p&gt;If you've ever wanted to let an AI code agent or experimental script run wild without worrying about it wreaking havoc on your system, you need isolation. While Docker is the go-to solution for many, &lt;strong&gt;systemd-nspawn&lt;/strong&gt; offers a compelling alternative that's built right into systemd-based Linux distributions.&lt;/p&gt;
&lt;h3 id="what-makes-systemd-nspawn-different"&gt;What Makes systemd-nspawn Different?&lt;/h3&gt;
&lt;p&gt;systemd-nspawn is a lightweight container manager that comes pre-installed with systemd. Think of it as "chroot on steroids" - it provides OS-level virtualization without the overhead of full virtual machines or even Docker's daemon architecture. Here's why it's particularly suited for development sandboxes:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ultra-lightweight&lt;/strong&gt;: Minimal overhead compared to VMs or Docker&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Native integration&lt;/strong&gt;: Uses systemd's built-in features, no extra daemon required&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fast startup&lt;/strong&gt;: Containers boot in seconds&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Full system containers&lt;/strong&gt;: Run complete init systems, perfect for testing systemd services&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No layered filesystem complexity&lt;/strong&gt;: Direct filesystem access makes debugging easier&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Resource efficient&lt;/strong&gt;: Multiple containers share the host kernel with minimal memory overhead&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Perfect Use Cases:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Testing destructive scripts or untrusted code&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Giving AI code agents (like Cursor, Opencode, Copilot, or Claude Code) a safe playground&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Development environments that mirror production&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Testing system-level changes without risking your host&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Learning system administration in isolation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Building and testing packages in clean environments&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guide demonstrates the setup on Arch Linux, but the concepts apply to any systemd-based distribution (Ubuntu, Debian, Fedora, etc.) with minor command adjustments.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before starting, ensure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A Linux system running systemd (most modern distributions)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Root or sudo access&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;arch-install-scripts&lt;/code&gt; package (provides &lt;code&gt;pacstrap&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Basic understanding of Linux command line&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At least 1-2 GB free disk space per container&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Install prerequisites on Arch Linux:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pacman&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;arch-install-scripts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="step-by-step-setup-guide"&gt;Step-by-Step Setup Guide&lt;/h2&gt;
&lt;h3 id="step-1-create-container-directory-structure"&gt;Step 1: Create Container Directory Structure&lt;/h3&gt;
&lt;p&gt;We'll create the container in your home directory for easy access, then symlink it to the standard systemd-nspawn location:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;~/containers/mycontainer
sudo&lt;span class="w"&gt; &lt;/span&gt;ln&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;~/containers/mycontainer&lt;span class="w"&gt; &lt;/span&gt;/var/lib/machines/mycontainer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why this approach?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Keeps containers in your home directory (easier backups, user control)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The symlink to &lt;code&gt;/var/lib/machines/&lt;/code&gt; lets systemd's &lt;code&gt;machinectl&lt;/code&gt; tools discover and manage it&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can create multiple containers by repeating with different names&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Directory Structure Explained:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;~/containers/&lt;/code&gt; - Your personal container storage&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;mycontainer&lt;/code&gt; - The root filesystem of your container (change this name as needed)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;/var/lib/machines/&lt;/code&gt; - Standard location where systemd looks for containers&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="step-2-install-base-system-with-pacstrap"&gt;Step 2: Install Base System with pacstrap&lt;/h3&gt;
&lt;p&gt;Bootstrap a minimal Arch Linux installation into the container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pacstrap&lt;span class="w"&gt; &lt;/span&gt;-K&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;~/containers/mycontainer&lt;span class="w"&gt; &lt;/span&gt;base&lt;span class="w"&gt; &lt;/span&gt;openssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Command breakdown:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;pacstrap&lt;/code&gt; - Arch's tool for installing packages to a new root&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-K&lt;/code&gt; - Initialize an empty pacman keyring in the container&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-c&lt;/code&gt; - Use the host's package cache (faster, saves bandwidth)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;base&lt;/code&gt; - Minimal Arch Linux base system&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;openssh&lt;/code&gt; - SSH server for remote access&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What's being installed?&lt;/strong&gt;
This creates a minimal bootable Linux system (~150-200 MB) with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Core utilities (coreutils, bash, systemd)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Package manager (pacman)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SSH server for remote access&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Basic networking tools&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Time estimate:&lt;/strong&gt; 2-5 minutes depending on your internet speed.&lt;/p&gt;
&lt;h3 id="step-3-set-root-password"&gt;Step 3: Set Root Password&lt;/h3&gt;
&lt;p&gt;Enter the container and set a root password for SSH access:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemd-nspawn&lt;span class="w"&gt; &lt;/span&gt;-D&lt;span class="w"&gt; &lt;/span&gt;~/containers/mycontainer
passwd
&lt;span class="nb"&gt;logout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What's happening here:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;systemd-nspawn -D&lt;/code&gt; spawns a shell in the container (like &lt;code&gt;chroot&lt;/code&gt; but better)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-D&lt;/code&gt; specifies the directory (root filesystem) of the container&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;passwd&lt;/code&gt; sets the root password inside the container&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;logout&lt;/code&gt; exits back to your host system&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Security note:&lt;/strong&gt; You're setting a password inside the container, not on your host. This isolation is key to the security model.&lt;/p&gt;
&lt;h3 id="step-4-enable-ssh-and-networking-in-container"&gt;Step 4: Enable SSH and Networking in Container&lt;/h3&gt;
&lt;p&gt;Boot the container with a full init system to properly enable services:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemd-nspawn&lt;span class="w"&gt; &lt;/span&gt;-bD&lt;span class="w"&gt; &lt;/span&gt;~/containers/mycontainer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Command breakdown:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-b&lt;/code&gt; (boot) - Starts systemd inside the container as PID 1 (full boot sequence)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Without &lt;code&gt;-b&lt;/code&gt;, you just get a shell; with &lt;code&gt;-b&lt;/code&gt;, you get a complete system&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After it boots, log in as root with your password, then run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sshd
systemctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;systemd-networkd
sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s/^#*PermitRootLogin.*/PermitRootLogin yes/&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/ssh/sshd_config
poweroff
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What each command does:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;systemctl enable sshd&lt;/code&gt; - Start SSH server automatically on boot&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;systemctl enable systemd-networkd&lt;/code&gt; - Enable networking on boot (for DHCP)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;sed&lt;/code&gt; command modifies SSH config to allow root login (needed for initial access)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;poweroff&lt;/code&gt; cleanly shuts down the container&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Networking Architecture:&lt;/strong&gt;
The container will get a virtual network interface (&lt;code&gt;host0&lt;/code&gt;) that connects to a virtual Ethernet pair on the host. This provides network isolation while still allowing internet access via NAT.&lt;/p&gt;
&lt;h3 id="step-5-configure-static-network-on-host"&gt;Step 5: Configure Static Network on Host&lt;/h3&gt;
&lt;p&gt;The host needs systemd-networkd to provide network connectivity for containers. First enable systemd-networkd:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--now&lt;span class="w"&gt; &lt;/span&gt;systemd-networkd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then create a static network configuration for the container's virtual interface:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vim&lt;span class="w"&gt; &lt;/span&gt;/etc/systemd/network/50-ve-mycontainer.network
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add the following configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;[Match]&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ve-mycontainer&lt;/span&gt;
&lt;span class="na"&gt;Driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;veth&lt;/span&gt;

&lt;span class="k"&gt;[Network]&lt;/span&gt;
&lt;span class="na"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;192.168.200.1/24&lt;/span&gt;
&lt;span class="na"&gt;LinkLocalAddressing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="na"&gt;DHCPServer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;no&lt;/span&gt;
&lt;span class="na"&gt;IPMasquerade&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="na"&gt;LLDP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="na"&gt;EmitLLDP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;customer-bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Restart the networking service to apply the configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;systemd-networkd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What this configuration does:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[Match]&lt;/code&gt; section targets the virtual Ethernet interface for your container (&lt;code&gt;ve-mycontainer&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[Network]&lt;/code&gt; section configures the host-side interface with a static IP &lt;code&gt;192.168.200.1/24&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;IPMasquerade=yes&lt;/code&gt; enables NAT so the container can access the internet through the host&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;DHCPServer=no&lt;/code&gt; disables DHCP since we'll use static addressing&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;LinkLocalAddressing=yes&lt;/code&gt; enables link-local addressing for fallback connectivity&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="step-6-start-container-with-machinectl"&gt;Step 6: Start Container with machinectl&lt;/h3&gt;
&lt;p&gt;Now use systemd's container management tool to start your container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;mycontainer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;About machinectl:&lt;/strong&gt;
&lt;code&gt;machinectl&lt;/code&gt; is systemd's high-level tool for managing containers and VMs. It handles:
- Starting/stopping containers
- Network setup (creates virtual Ethernet pairs)
- Integration with systemd's journal (logging)
- Resource limits and cgroups&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What's happening behind the scenes:&lt;/strong&gt;
1. systemd creates a virtual Ethernet pair (veth)
2. One end goes into the container (&lt;code&gt;host0&lt;/code&gt;), one stays on host (&lt;code&gt;ve-mycontainer&lt;/code&gt;)
3. The container boots with systemd as init
4. The host interface gets configured with static IP &lt;code&gt;192.168.200.1/24&lt;/code&gt;
5. SSH server starts automatically
6. We'll configure the container with static IP &lt;code&gt;192.168.200.200/24&lt;/code&gt; in the next step&lt;/p&gt;
&lt;h3 id="step-7-configure-container-static-network"&gt;Step 7: Configure Container Static Network&lt;/h3&gt;
&lt;p&gt;Shell into the container and configure the static network:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;mycontainer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create the static network config inside the container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/systemd/network/80-container-host0.network&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; &amp;#39;EOF&amp;#39;&lt;/span&gt;
&lt;span class="s"&gt;[Match]&lt;/span&gt;
&lt;span class="s"&gt;Name=host0&lt;/span&gt;

&lt;span class="s"&gt;[Network]&lt;/span&gt;
&lt;span class="s"&gt;DHCP=no&lt;/span&gt;
&lt;span class="s"&gt;Address=192.168.200.200/24&lt;/span&gt;
&lt;span class="s"&gt;Gateway=192.168.200.1&lt;/span&gt;
&lt;span class="s"&gt;DNS=192.168.200.1&lt;/span&gt;

&lt;span class="s"&gt;[Link]&lt;/span&gt;
&lt;span class="s"&gt;RequiredForOnline=yes&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Restart networking inside the container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;systemd-networkd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Check the result:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ip&lt;span class="w"&gt; &lt;/span&gt;-4&lt;span class="w"&gt; &lt;/span&gt;addr&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;host0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Expected output:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;host0&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;if9&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BROADCAST&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;MULTICAST&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;LOWER_UP&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mtu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qdisc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;noqueue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qlen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;inet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;192.168&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;200.200&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;brd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;192.168&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;200.255&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;host0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Exit the container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Reboot the container to ensure all services start properly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;reboot&lt;span class="w"&gt; &lt;/span&gt;mycontainer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="step-8-ssh-into-your-container"&gt;Step 8: SSH into Your Container&lt;/h3&gt;
&lt;p&gt;Connect to the container via SSH from your host using the static IP:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;root@192.168.200.200
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What you can do now:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Full isolated Linux environment&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install packages (&lt;code&gt;pacman -S ...&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run experiments, destructive scripts, untrusted code&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test system configurations&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Give AI agents unrestricted access without risk&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example use case - AI code agent:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# From your host, give an AI agent access to the container&lt;/span&gt;
ssh&lt;span class="w"&gt; &lt;/span&gt;root@192.168.200.200&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cd /workspace &amp;amp;&amp;amp; ai-agent-run --dangerous-mode&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If something breaks, just destroy and recreate the container in seconds!&lt;/p&gt;
&lt;h2 id="container-management-commands"&gt;Container Management Commands&lt;/h2&gt;
&lt;p&gt;Here's your complete toolkit for managing containers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Start a container (if stopped)&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Stop a running container (graceful shutdown)&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Restart a container&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Enable container to start at boot&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Disable auto-start&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;disable&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Check container status and IP address&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Get an interactive shell (without SSH)&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Login to console (like physical machine access)&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;login&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Execute a single command in container&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;mycontainer&lt;span class="w"&gt; &lt;/span&gt;/usr/bin/command

&lt;span class="c1"&gt;# List all containers&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;list

&lt;span class="c1"&gt;# Show container properties&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Power off immediately (hard stop)&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;poweroff&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Remove/unregister a container (doesn&amp;#39;t delete files)&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;remove&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Copy files to container&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;copy-to&lt;span class="w"&gt; &lt;/span&gt;mycontainer&lt;span class="w"&gt; &lt;/span&gt;/local/file&lt;span class="w"&gt; &lt;/span&gt;/container/path

&lt;span class="c1"&gt;# Copy files from container&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;copy-from&lt;span class="w"&gt; &lt;/span&gt;mycontainer&lt;span class="w"&gt; &lt;/span&gt;/container/file&lt;span class="w"&gt; &lt;/span&gt;/local/path
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="advanced-tips-and-troubleshooting"&gt;Advanced Tips and Troubleshooting&lt;/h2&gt;
&lt;h3 id="resource-limits"&gt;Resource Limits&lt;/h3&gt;
&lt;p&gt;Limit container resources using systemd slices:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Limit CPU (50%)&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;set-property&lt;span class="w"&gt; &lt;/span&gt;systemd-nspawn@mycontainer.service&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CPUQuota&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;%

&lt;span class="c1"&gt;# Limit RAM (1GB)&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;set-property&lt;span class="w"&gt; &lt;/span&gt;systemd-nspawn@mycontainer.service&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MemoryMax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8G

&lt;span class="c1"&gt;# Limit I/O&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;set-property&lt;span class="w"&gt; &lt;/span&gt;systemd-nspawn@mycontainer.service&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;IOWeight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id="how-it-works"&gt;How it works:&lt;/h4&gt;
&lt;p&gt;When machinectl start mycontainer runs, systemd creates a systemd-nspawn@mycontainer.service unit&lt;/p&gt;
&lt;p&gt;The @ syntax indicates it's an instantiated service template&lt;/p&gt;
&lt;p&gt;systemd-nspawn@ is the template, mycontainer is the instance name&lt;/p&gt;
&lt;p&gt;You can verify the service name with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;systemd-nspawn@mycontainer.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="persistent-storage"&gt;Persistent Storage&lt;/h3&gt;
&lt;p&gt;Mount host directories into containers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Start with bind mount&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;systemd-nspawn&lt;span class="w"&gt; &lt;/span&gt;-bD&lt;span class="w"&gt; &lt;/span&gt;~/containers/mycontainer&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--bind&lt;span class="o"&gt;=&lt;/span&gt;/home/user/projects:/workspace
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or configure in &lt;code&gt;/etc/systemd/nspawn/mycontainer.nspawn&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;[Files]&lt;/span&gt;
&lt;span class="na"&gt;Bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/user/projects:/workspace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="creating-multiple-containers"&gt;Creating Multiple Containers&lt;/h3&gt;
&lt;p&gt;Easily spin up multiple isolated environments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Create second container&lt;/span&gt;
mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;~/containers/testbox
sudo&lt;span class="w"&gt; &lt;/span&gt;ln&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;~/containers/testbox&lt;span class="w"&gt; &lt;/span&gt;/var/lib/machines/testbox
sudo&lt;span class="w"&gt; &lt;/span&gt;pacstrap&lt;span class="w"&gt; &lt;/span&gt;-K&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;~/containers/testbox&lt;span class="w"&gt; &lt;/span&gt;base&lt;span class="w"&gt; &lt;/span&gt;openssh
&lt;span class="c1"&gt;# Repeat configuration steps...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="cleanup-and-reset"&gt;Cleanup and Reset&lt;/h3&gt;
&lt;p&gt;Destroy a container completely:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Stop container&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Remove from systemd&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;machinectl&lt;span class="w"&gt; &lt;/span&gt;disable&lt;span class="w"&gt; &lt;/span&gt;mycontainer

&lt;span class="c1"&gt;# Delete filesystem&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;~/containers/mycontainer
sudo&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;/var/lib/machines/mycontainer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then recreate from Step 1 for a fresh start!&lt;/p&gt;
&lt;h3 id="common-issues"&gt;Common Issues&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Container won't start:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Check logs: &lt;code&gt;sudo journalctl -u systemd-nspawn@mycontainer.service&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify symlink: &lt;code&gt;ls -la /var/lib/machines/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;No network in container:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Verify systemd-networkd: &lt;code&gt;systemctl status systemd-networkd&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check host network config: &lt;code&gt;cat /etc/systemd/network/50-ve-mycontainer.network&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check container network config: &lt;code&gt;sudo machinectl shell mycontainer cat /etc/systemd/network/80-container-host0.network&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Restart networkd: &lt;code&gt;sudo systemctl restart systemd-networkd&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify container IP: &lt;code&gt;sudo machinectl shell mycontainer ip -4 addr show host0&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;SSH connection refused:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Verify sshd is running in container: &lt;code&gt;sudo machinectl shell mycontainer systemctl status sshd&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check SSH config: &lt;code&gt;sudo machinectl shell mycontainer cat /etc/ssh/sshd_config | grep PermitRootLogin&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try direct shell first: &lt;code&gt;sudo machinectl login mycontainer&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="security-considerations"&gt;Security Considerations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Isolation Level:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;systemd-nspawn provides &lt;strong&gt;OS-level virtualization&lt;/strong&gt;, not full VM isolation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Containers share the host kernel (like Docker)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Not suitable for hostile/untrusted multi-tenant scenarios&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Perfect for development sandboxing and testing&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Hardening Options:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Start with additional security&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;systemd-nspawn&lt;span class="w"&gt; &lt;/span&gt;-bD&lt;span class="w"&gt; &lt;/span&gt;~/containers/mycontainer&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--private-network&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# No network access&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--read-only&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# Read-only root filesystem&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--drop-capability&lt;span class="o"&gt;=&lt;/span&gt;all&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Drop all Linux capabilities&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Best Practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use non-root users inside containers when possible&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Keep containers updated: &lt;code&gt;sudo machinectl shell mycontainer pacman -Syu&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;code&gt;--ephemeral&lt;/code&gt; flag for truly disposable environments&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Consider SELinux/AppArmor for additional isolation&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Choose systemd-nspawn when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You need full OS containers for testing&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You want minimal dependencies (systemd only)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You're testing systemd services specifically&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You want direct filesystem access for debugging&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Choose Docker when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You need portable, reproducible builds&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You're deploying applications to production&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You want a rich ecosystem of pre-built images&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You need orchestration (Kubernetes, Swarm)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;systemd-nspawn provides a lightweight, powerful way to create isolated development environments without the complexity of full virtualization or container orchestration platforms. It's ideal for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Safe experimentation&lt;/strong&gt; with code that might be destructive&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AI code agent sandboxing&lt;/strong&gt; where you want to give tools unrestricted access&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;System-level testing&lt;/strong&gt; of services and configurations&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Development environments&lt;/strong&gt; that mirror production systems&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Learning Linux&lt;/strong&gt; without fear of breaking your main system&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tight integration with systemd means you get enterprise-grade container management with tools you already have installed. Start simple, experiment freely, and destroy/recreate at will!&lt;/p&gt;
&lt;h2 id="further-reading"&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://wiki.archlinux.org/title/Systemd-nspawn"&gt;Arch Wiki: systemd-nspawn&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html"&gt;systemd-nspawn man page&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.freedesktop.org/software/systemd/man/machinectl.html"&gt;machinectl man page&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://systemd.io/CONTAINER_INTERFACE/"&gt;Container Interface Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content><category term="Software"/><category term="linux"/><category term="containers"/><category term="systemd"/><category term="arch"/><category term="development"/><category term="security"/></entry><entry><title>CPU specific optimized Python on AWS</title><link href="https://adamgradzki.com/cpu-specific-optimized-python-on-aws.html" rel="alternate"/><published>2022-11-30T00:00:00-05:00</published><updated>2022-11-30T00:00:00-05:00</updated><author><name>Adam Gradzki</name></author><id>tag:adamgradzki.com,2022-11-30:/cpu-specific-optimized-python-on-aws.html</id><summary type="html">&lt;p&gt;Build an optimized Python on AWS Gravitron ARM for better performance and cloud cost savings&lt;/p&gt;</summary><content type="html">&lt;p&gt;Recently there has been a need to more efficiently host our software services on AWS. As of publication this is most readily achievable for general purpose Python code with (AWS Graviton instances)[https://aws.amazon.com/ec2/graviton/]&lt;/p&gt;
&lt;p&gt;Starting from a Debian 11 ARM EC2 t4g instance, the following commands are able to create a Python build from source optimized for the CPU architecture running on AWS. Note, LTO is enabled in the build script and it uses a huge amount of RAM, so make sure you have enough RAM or use a swapfile as I demonstrate below since I chose a t4g.small instance.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;-y
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;upgrade&lt;span class="w"&gt; &lt;/span&gt;-y
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;build-essential&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;gdb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;lcov&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libbz2-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libffi-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libgdbm-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;liblzma-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libncurses5-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libreadline6-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libsqlite3-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libssl-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;lzma&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;lzma-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;tk-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;uuid-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libxml2-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libxml2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libxslt1-dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;libxslt1.1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;xvfb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;zlib1g-dev
apt&lt;span class="w"&gt; &lt;/span&gt;build-dep&lt;span class="w"&gt; &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;-y
curl&lt;span class="w"&gt; &lt;/span&gt;https://pyenv.run&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash

&lt;span class="c1"&gt;# If you have less than 8 GB RAM, create a swapfile and enable it&lt;/span&gt;
&lt;span class="c1"&gt;# Don&amp;#39;t put the swapfile on EBS.&lt;/span&gt;
&lt;span class="c1"&gt;# It should be on your instance root volume for performance.&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;fallocate&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;8G&lt;span class="w"&gt; &lt;/span&gt;/swapfile
sudo&lt;span class="w"&gt; &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/swapfile
sudo&lt;span class="w"&gt; &lt;/span&gt;mkswap&lt;span class="w"&gt; &lt;/span&gt;/swapfile
sudo&lt;span class="w"&gt; &lt;/span&gt;swapon&lt;span class="w"&gt; &lt;/span&gt;/swapfile

&lt;span class="c1"&gt;################################################&lt;/span&gt;
&lt;span class="c1"&gt;# Append the following to the end of ~/.bashrc #&lt;/span&gt;
&lt;span class="c1"&gt;################################################&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PYENV_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.pyenv&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;/dev/null&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PYENV_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;init&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Restart your shell for the changes to take effect.&lt;/span&gt;

&lt;span class="c1"&gt;# Load pyenv-virtualenv automatically by adding&lt;/span&gt;
&lt;span class="c1"&gt;# the following to ~/.bashrc:&lt;/span&gt;

&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;virtualenv-init&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;################################################&lt;/span&gt;
&lt;span class="c1"&gt;#        Restart your shell to continue        #&lt;/span&gt;
&lt;span class="c1"&gt;################################################&lt;/span&gt;

&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-march=native -mtune=native&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CONFIGURE_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;--enable-optimizations --with-lto=full&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.11.1&lt;span class="w"&gt; &lt;/span&gt;--verbose
pyenv&lt;span class="w"&gt; &lt;/span&gt;virtualenv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.11.1&lt;span class="w"&gt; &lt;/span&gt;MY_VIRTUAL_ENVIRONMENT_NAME
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="Software"/><category term="python"/><category term="debian"/><category term="aws"/></entry><entry><title>Pronounce foreign words with an open source speech therapist</title><link href="https://adamgradzki.com/pronounce-foreign-words-with-an-open-source-speech-therapist.html" rel="alternate"/><published>2022-03-01T00:00:00-05:00</published><updated>2022-03-01T00:00:00-05:00</updated><author><name>Adam Gradzki</name></author><id>tag:adamgradzki.com,2022-03-01:/pronounce-foreign-words-with-an-open-source-speech-therapist.html</id><summary type="html">&lt;p&gt;Open source software can assist with pronounciation of languages if you don't have someone to talk to right now.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Learning to pronounce words without a tutor is so much easier thanks to the open source software espeak-ng. &lt;a href="https://github.com/espeak-ng/espeak-ng/blob/master/src/espeak-ng.1.ronn#examples"&gt;Link to usage examples.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I can get sound and the IPA spelling of any word easily directly from a free offline program installed on my laptop. For example, the following command gives the German pronounciation of the word "meine" with the fifth female robotic voice actor, further slowed down to a crawl so I can hear it better with the "-s 80" flag:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;espeak-ng&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;de+f5&lt;span class="w"&gt; &lt;/span&gt;--ipa&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;meine&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;
mˈaɪnə
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/numediart/MBROLA-voices/blob/master/README.md"&gt;It is also possible to upgrade the speech engine by installing MBROLA voices for improved synthesis&lt;/a&gt;&lt;/p&gt;</content><category term="Language Learning"/><category term="language"/></entry><entry><title>Golang for loops and the range-for</title><link href="https://adamgradzki.com/golang-for-loops-and-the-range-for.html" rel="alternate"/><published>2020-06-17T00:00:00-04:00</published><updated>2020-06-17T00:00:00-04:00</updated><author><name>Adam Gradzki</name></author><id>tag:adamgradzki.com,2020-06-17:/golang-for-loops-and-the-range-for.html</id><summary type="html">&lt;p&gt;The for loop you choose in golang may have performance implications&lt;/p&gt;</summary><content type="html">&lt;p&gt;I was unable to find a find a clear answer online to what are the performance implications of using a standard for loop (e.g., for i := 0; i &amp;lt; len(arr); i++ {}) vs range-for (e.g., for idx, val := range arr {}) in Golang. By analyzing the assembler output I determined that as of Go 1.14 they produce similar CPU instructions for common 64-bit Intel/AMD CPUs.&lt;/p&gt;
&lt;p&gt;The range-for loop produces two additional assembly instructions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;movq&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;.arr&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;SP&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;AX&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This brings the total instruction count for function count2 to 13 compared to 11 for function count1.&lt;/p&gt;
&lt;p&gt;I used https://go.godbolt.org/ at the suggestion of dominikh on Freenode IRC #golang to map the Golang functions with the corresponding assembler output.&lt;/p&gt;
&lt;p&gt;My code for an apples-to-apples comparison between the two:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;count1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;count2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;count1() produces:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;movq&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;.arr&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;SP&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;AX&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;xorl&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;CX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;CX&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;jmp&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;count1_pc12&lt;/span&gt;
&lt;span class="nl"&gt;count1_pc9:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;incq&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;CX&lt;/span&gt;
&lt;span class="nl"&gt;count1_pc12:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;cmpq&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;CX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;AX&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;jlt&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;count1_pc9&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;ret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;count2() produces:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;movq&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;.arr&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;SP&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;AX&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;movq&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;AX&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;AX&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;xorl&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;CX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;CX&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;jmp&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;count2_pc16&lt;/span&gt;
&lt;span class="nl"&gt;count2_pc13:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;incq&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;CX&lt;/span&gt;
&lt;span class="nl"&gt;count2_pc16:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;cmpq&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;CX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;AX&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;jlt&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;count2_pc13&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;pcdata&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;ret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;count2 produces more assembler instructions which &lt;em&gt;may&lt;/em&gt; suggest it is slower code. However, this is not always the case. To understand the real performance implications of these instructions benchmarks need to be conducted in a future article.&lt;/p&gt;</content><category term="Software"/><category term="golang"/><category term="loop"/></entry><entry><title>Faster Virtual Machines on Linux Hosts with GPU Acceleration</title><link href="https://adamgradzki.com/faster-virtual-machines-on-linux-hosts-with-gpu-acceleration.html" rel="alternate"/><published>2020-04-06T00:00:00-04:00</published><updated>2020-04-08T00:00:00-04:00</updated><author><name>Adam Gradzki</name></author><id>tag:adamgradzki.com,2020-04-06:/faster-virtual-machines-on-linux-hosts-with-gpu-acceleration.html</id><summary type="html">&lt;p&gt;Virtual machines don't all draw to screen efficiently, but some new approaches are getting there.&lt;/p&gt;</summary><content type="html">&lt;div class="toc"&gt;&lt;span class="toctitle"&gt;Table of Contents&lt;/span&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#overview"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#gpu-command-architectures"&gt;GPU command architectures&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#vga-emulation-ve"&gt;VGA Emulation (VE)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#api-forwarding-af"&gt;API forwarding (AF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#direct-pass-through-dpt"&gt;Direct Pass-Through (DPT)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#full-gpu-virtualization-fgv"&gt;Full GPU Virtualization (FGV)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#hypervisors"&gt;Hypervisors&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#oracle-virtualbox"&gt;Oracle VirtualBox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#vmware-vsphereesxi"&gt;VMWare vSphere/ESXi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#vmware-workstation"&gt;VMWare Workstation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#qemu"&gt;QEMU&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="overview"&gt;Overview&lt;/h1&gt;
&lt;p&gt;Open source virtualization technologies widely available in the Linux software ecosystem lack the ease of use of graphical performance enhancements available in commercial virtualization technologies such as &lt;a href="https://www.vmware.com/products/workstation-pro.html"&gt;VMWare Workstation&lt;/a&gt; or &lt;a href="https://www.vmware.com/products/esxi-and-esx.html"&gt;VMWare vSphere/ESXi&lt;/a&gt;. &lt;a href="https://projectacrn.github.io/latest/developer-guides/hld/hld-APL_GVT-g.html"&gt;Intel GVT-g&lt;/a&gt; is a virtual graphics acceleration technology which can be accessed with the &lt;a href="https://www.qemu.org/"&gt;QEMU&lt;/a&gt; virtualization system. &lt;a href="https://www.qemu.org/"&gt;QEMU&lt;/a&gt; serves as an open-source alternative to technologies such as &lt;a href="https://www.vmware.com/products/workstation-pro.html"&gt;VMWare Workstation&lt;/a&gt; or &lt;a href="https://www.vmware.com/products/esxi-and-esx.html"&gt;VMWare vSphere/ESXi&lt;/a&gt;. &lt;a href="https://projectacrn.github.io/latest/developer-guides/hld/hld-APL_GVT-g.html"&gt;Intel GVT-g&lt;/a&gt; was configured on a &lt;a href="https://wiki.archlinux.org/index.php/Lenovo_ThinkPad_X1_Carbon_(Gen_6)"&gt;Thinkpad X1 Generation 6 laptop&lt;/a&gt; containing Intel integrated graphics resulting in successful GPU acceleration on a UEFI Windows 10 64-bit guest without relying on proprietary software aside from the guest operating system itself. Substantially improved virtualization performance is possible due to working Intel GVT-g GPU acceleration on Linux hosts.&lt;/p&gt;
&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Computer users rely on software written for many different operating systems. Virtual machines allow computer users to simultaneously run different operating systems and switch between them easily. &lt;a href="https://www.vmware.com/solutions/virtualization.html"&gt;Virtualization has benefits&lt;/a&gt; such as being able to migrate installed systems to other physical machines with lower downtime, the ability to contain &lt;a href="https://rosettacode.org/wiki/Untrusted_environment"&gt;untrusted code&lt;/a&gt; in a sandbox that is difficult to escape from, maintain operation of legacy systems that are difficult to keep running on obsolete hardware, or simply running a Windows-only program on a Linux &lt;a href="https://pediaa.com/what-is-the-difference-between-host-and-guest-operating-system/"&gt;host&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Virtual machines with graphical user interfaces typically suffer from input lag and stuttering, both of which lead to a degraded user experience. Additionally, software which relies on heavy computation such as photo editing or engineering is dependent on efficient GPU access to speed up calculations by an order of magnitude or more over the host machine CPU to finish calculations in a reasonable time period. Unfortunately, not all virtualization solutions are able to leverage the physical chips on the host machine in an efficient manner, regardless of cost.&lt;/p&gt;
&lt;h1 id="gpu-command-architectures"&gt;GPU command architectures&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;VGA Emulation (VE)&lt;ul&gt;
&lt;li&gt;Universally available on all virtualization platforms&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;API forwarding (AF)&lt;ul&gt;
&lt;li&gt;Intel GVT-s&lt;/li&gt;
&lt;li&gt;VMWare Virtual Shared Graphics Acceleration (vSGA)&lt;/li&gt;
&lt;li&gt;Oracle VirtualBox 3D Acceleration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Direct Pass-Through (DPT)&lt;ul&gt;
&lt;li&gt;Intel GVT-d&lt;/li&gt;
&lt;li&gt;VMWare Virtual Dedicated Graphics Acceleration (vDGA)&lt;/li&gt;
&lt;li&gt;Not available in VMWare Workstation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Full GPU Virtualization (FGV)&lt;ul&gt;
&lt;li&gt;Intel GVT-g&lt;/li&gt;
&lt;li&gt;VMWare Virtual Shared Pass-Through Graphics Acceleration (vGPU or MxGPU)&lt;/li&gt;
&lt;li&gt;Not available in VMWare Workstation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="vga-emulation-ve"&gt;VGA Emulation (VE)&lt;/h2&gt;
&lt;p&gt;The most primitive graphics display for any virtual machine is VGA Emulation (VE). This mode is also the most inefficient. &lt;a href="https://github.com/qemu/qemu/blob/master/hw/display/cirrus_vga.c"&gt;QEMU emulates a Cirrus Logic GD5446 Video card.&lt;/a&gt; All Windows versions starting from Windows 95 should recognize and use this graphic card.&lt;/p&gt;
&lt;p&gt;Most hypervisors which advertise some form of &lt;a href="https://developer.ibm.com/technologies/linux/tutorials/l-pci-passthrough/"&gt;"hardware acceleration"&lt;/a&gt; use API Forwarding (AF), which is a high performance proxy service that requires specialized drivers on both the host and guest to create a high performance instruction pipeline.&lt;/p&gt;
&lt;h2 id="api-forwarding-af"&gt;API forwarding (AF)&lt;/h2&gt;
&lt;p&gt;API Forwarding (AF) works by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;intercepting the GPU command requested by a piece of software&lt;/li&gt;
&lt;li&gt;proxying the GPU command to the host hypervisor&lt;/li&gt;
&lt;li&gt;executing the captured GPU command on the host from the hypervisor&lt;/li&gt;
&lt;li&gt;bubbling the response back up to the virtual machine&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This mode is very useful when many virtual machines are competing for resources of a single GPU and Full GPU Virtualization (FGV) is not possible. The hypervisor queues graphics card operations from one or more virtual machine and schedules virtual execution and memory slots for each virtual machine on a single physical GPU resource. Each virtual machine sees its own graphics card while the hypervisor splits the single physical resource up. A key drawback of AF is that usually only OpenGL and DirectX interfaces are supported by the GPU instruction proxy.&lt;/p&gt;
&lt;p&gt;The process by which API Forwarding (AF) works is known as &lt;a href="https://www.unf.edu/~sahuja/cloudcourse/Fullandparavirtualization.pdf"&gt;paravirtualization&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="direct-pass-through-dpt"&gt;Direct Pass-Through (DPT)&lt;/h2&gt;
&lt;p&gt;Direct Pass-Through (DPT) is a system which exposes the GPU as a PCI device which is directly addressable by the virtual machine. Nothing besides the virtual machine can reference any resources on the GPU and it cannot be shared with the physical machine or any other virtual machines. Many devices have only one graphics card installed and using this system would mean making the graphical user interface inoperable. This method is most useful when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the physical graphics card does not support Full GPU Virtualization (FGV)&lt;/li&gt;
&lt;li&gt;two or more graphics cards are attached to a system&lt;/li&gt;
&lt;li&gt;paravirtualized drivers are not available or do not work with the installed physical GPU, host hypervisor, or guest operating system&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="full-gpu-virtualization-fgv"&gt;Full GPU Virtualization (FGV)&lt;/h2&gt;
&lt;p&gt;Sharing a GPU natively among multiple virtual machines is possible with Full GPU Virtualization (FGV) solutions such as Intel GVT-g. This process is also known as Hardware Assisted Virtualization (HVM), not to be confused with Paravirtualization (PV). In this mode the IOMMU hardware exposes a GPU memory interface to each virtual machine while it internally handles the memory address mappings between what it exposes to virtual machines and the actual physical memory on the GPU.&lt;/p&gt;
&lt;p&gt;In "IOMMU and Virtualization," &lt;a href="https://www.linkedin.com/in/susanta"&gt;Susanta Nanda&lt;/a&gt; &lt;a href="https://vmmworld.blogspot.com/2006/05/iommu-and-virtualization.html"&gt;writes&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;IOMMU provides two main functionalities: virtual-to-physical address translation and access protection on the memory ranges that an I/O device is trying to operate on. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Peak media workload performance is 95% of the native host alone when running one virtual machine and the average performance is 85% of the native host alone on media workloads according to Intel engineer Zhenyu Wang in &lt;a href="https://www.x.org/wiki/Events/XDC2017/wang_gvt.pdf"&gt;XDC2017 presentation "Full GPU virtualization in mediated pass-through way"&lt;/a&gt; &lt;/p&gt;
&lt;h1 id="hypervisors"&gt;Hypervisors&lt;/h1&gt;
&lt;p&gt;The hypervisors I use are software systems that enable multiple virtual machines to run simultaneously on a single physical machine. Linux users have many &lt;a href="https://en.wikipedia.org/wiki/Hypervisor"&gt;hypervisor&lt;/a&gt; options. &lt;a href="https://opensourceforu.com/2019/04/the-top-open-source-hypervisor-technologies/"&gt;A longer list is available here&lt;/a&gt;. These are a sample of some Linux hypervisors:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.virtualbox.org/"&gt;Oracle VirtualBox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.vmware.com/products/esxi-and-esx.html"&gt;VMWare vSphere/ESXi&lt;/a&gt; (technically runs beneath Linux)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.vmware.com/products/workstation-pro.html"&gt;VMWare Workstation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.qemu.org/"&gt;QEMU&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="oracle-virtualbox"&gt;Oracle VirtualBox&lt;/h2&gt;
&lt;p&gt;Possibly the most widely-used hypervisor is &lt;a href="https://www.virtualbox.org/"&gt;VirtualBox by Oracle&lt;/a&gt;. VirtualBox is open source software with the exception of the optional extension pack.&lt;/p&gt;
&lt;p&gt;The Oracle VirtualBox extension pack provides many features which are not available in the free version. The PCI passthrough module was shipped as a Oracle VM VirtualBox extension package until the &lt;a href="https://github.com/mdaniel/virtualbox-org-svn-vbox-trunk/commit/5178e479b2ac1e33454f203854de9fe8f85a9196"&gt;feature was scrapped&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These features are available gratis for personal and non-commerical use only.&lt;/p&gt;
&lt;p&gt;Possession of the VirtualBox Extension Pack without a license &lt;a href="https://www.reddit.com/r/sysadmin/comments/8ffcg3/oracle_is_looking_under_the_couch_cushions_for/"&gt;can be problematic&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Got an email today informing me (Urgent Virtual Box Licensing Information for Company X) that there have been TWELVE (12!) downloads of the VirtualBox Extension Pack at my employer in the past year. And since the extensions are licensed differently than the base product, they'd love for us to call them and talk about how much money we owe them. Their report attached to email listed source IPs and AS number, as well as date/product/version. Out of the twelve (12!), there were always two on the same day of the same version, so really six (6!) downloads. We'll probably end up giving them $150, and I'll make sure they never get any business from places I work, because fuck Oracle. I wouldn't piss on Larry Ellison if he was on fire.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;VirtualBox Linux hosts do not support GPU DPT (Direct Pass-Through) at all. All of the preliminary PCI pass-through work for Linux hosts which is needed for GPU DPT was &lt;a href="https://github.com/mdaniel/virtualbox-org-svn-vbox-trunk/commit/5178e479b2ac1e33454f203854de9fe8f85a9196"&gt;completely stripped out on December 5th, 2019&lt;/a&gt; with this message:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Linux host: Drop PCI passthrough, the current code is too incomplete (cannot handle PCIe devices at all), i.e. not useful enough&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;VirtualBox 2D and 3D acceleration both work &lt;a href="https://docs.oracle.com/en/virtualization/virtualbox/6.1/user/guestadd-video.html"&gt;according to the same principle&lt;/a&gt;: API forwarding (AF)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Oracle VM VirtualBox implements 3D acceleration by installing an additional hardware 3D driver inside the guest when the Guest Additions are installed. This driver acts as a hardware 3D driver and reports to the guest operating system that the virtual hardware is capable of 3D hardware acceleration. When an application in the guest then requests hardware acceleration through the OpenGL or Direct3D programming interfaces, these are sent to the host through a special communication tunnel implemented by Oracle VM VirtualBox. The host then performs the requested 3D operation using the host's programming interfaces.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="vmware-vsphereesxi"&gt;VMWare vSphere/ESXi&lt;/h2&gt;
&lt;p&gt;VMWare vSphere/ESXi is a bare metal hypervisor which runs beneath any end-user operating systems. This property makes it a &lt;a href="https://www.ibm.com/cloud/learn/hypervisors"&gt;Type 1 Hypervisor&lt;/a&gt;. It supports all GPU acceleration technologies.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Virtual Shared Graphics Acceleration (vSGA) is a form of API forwarding (AF).&lt;/li&gt;
&lt;li&gt;Virtual Dedicated Graphics Acceleration (vDGA) technology is a form of Direct Pass-Through (DPT).&lt;/li&gt;
&lt;li&gt;VMWare Virtual Shared Pass-Through Graphics Acceleration (vGPU or MxGPU) is a form of Full GPU Virtualization (FGV).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main limitation of VMWare vSphere/ESXi GPU acceleration is the graphics card selection. GPU passthrough is possible only with a small set of GPUs because NVIDIA drivers disable consumer-market GPUs such as the GeForce series when the drivers detect that they are running in a virtual environment. &lt;a href="https://www.vmware.com/resources/compatibility/search.php?deviceCategory=sptg&amp;amp;details=1"&gt;Comprehensive list of all supported graphics cards for any hardware acceleration purposes.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="vmware-workstation"&gt;VMWare Workstation&lt;/h2&gt;
&lt;p&gt;VMWare Workstation only provides Virtual Shared Graphics Acceleration (vSGA), a form of API forwarding (AF). In this regard, the GPU acceleration story is identical to Oracle VirtualBox.&lt;/p&gt;
&lt;h2 id="qemu"&gt;QEMU&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.qemu.org/"&gt;QEMU&lt;/a&gt; is an open source virtual machine platform that is also capable of translating instructions between wholly unrelated computer architectures. It is widely available in most Linux distributions and is used extensively in industry.&lt;/p&gt;
&lt;p&gt;After enabling GVT-g in QEMU you must also recompile QEMU with the 60 fps fix to get smooth video as of the publication date of this article.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;QEMU eclipses VirtualBox in features and exceeds VMWare capabilities. VirtualBox is limited to API forwarding (AF) 
since it is not able to allow virtual machines to address graphics hardware directly in any way. VMWare solutions 
support all types of GPU addressing but most graphic cards made by NVIDIA disable themselves when they detect being 
called in Direct Pass-Through (DPT) or Full GPU Virtualization (FGV) modes. QEMU exceeds the features and provides 
hardware use flexibility beyond that of VMWare to bring near-native graphics performance to guest operating systems 
such as Windows 10 with truly minimal driver support required in the guest operating system. I recommend using QEMU 
on Linux when high graphics performance and low operational costs are prioritized for deploying a virtual machine 
environment.&lt;/p&gt;
&lt;!---
References (TODO)

https://software.intel.com/en-us/blogs/2009/06/25/understanding-vt-d-intel-virtualization-technology-for-directed-io
http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/vt-directed-io-spec.pdf
https://wiki.qemu.org/Features/VT-d
http://neg-serg.github.io/2017/06/pci-pass/
https://wiki.gentoo.org/wiki/QEMU
https://www.reddit.com/r/VFIO/comments/fn20eu/doom_eternal_cpu_host_needed/
https://blog.wikichoon.com/2014/07/enabling-hyper-v-enlightenments-with-kvm.html
https://blog.bepbep.co/posts/gvt/
https://nixos.wiki/wiki/IGVT-g
https://null-src.com/posts/qemu-vfio-pci/post.php
https://github.com/intel/gvt-linux/wiki/Dma_Buf_User_Guide#b-gtk
https://gist.github.com/mcastelino/7ab9dba51b0dbb230bd18c448d935312
https://bugzilla.redhat.com/show_bug.cgi?id=1337510
https://github.com/cardi/qemu-windows-10
https://github.com/cardi/qemu-windows-10/blob/master/start.sh
https://dennisnotes.com/note/20180614-ubuntu-18.04-qemu-setup/
https://wiki.archlinux.org/index.php/Intel_GVT-g#Using_DMA-BUF_display

---&gt;</content><category term="Software"/><category term="virtualization"/><category term="intel"/><category term="gvt-g"/><category term="arch-linux"/></entry><entry><title>Thinkpad X1 Carbon GPU Undervolting in Arch Linux</title><link href="https://adamgradzki.com/thinkpad-x1-carbon-gpu-undervolting-in-arch-linux.html" rel="alternate"/><published>2020-04-03T00:00:00-04:00</published><updated>2022-11-27T00:00:00-05:00</updated><author><name>Adam Gradzki</name></author><id>tag:adamgradzki.com,2020-04-03:/thinkpad-x1-carbon-gpu-undervolting-in-arch-linux.html</id><summary type="html">&lt;p&gt;Decrease your laptop CPU voltage to increase battery life&lt;/p&gt;</summary><content type="html">&lt;p&gt;After successfully installing &lt;a href="https://www.archlinux.org/about/"&gt;Arch Linux&lt;/a&gt; on my &lt;a href="https://www.notebookcheck.net/Lenovo-ThinkPad-X1-Carbon-2018-20KHCTO1WW.319032.0.html"&gt;Thinkpad X1 Carbon Generation 6&lt;/a&gt; I began experimenting with laptop component &lt;a href="https://www.techopedia.com/definition/26921/undervolting"&gt;undervolting&lt;/a&gt; to reduce heat and improve performance. Unfortunately it seems that the set points are being ignored to some extent.&lt;/p&gt;
&lt;p&gt;Indeed, &lt;a href="https://github.com/erpalma/throttled/issues/185#issuecomment-608279702"&gt;Francesco Palmarini reported on Github&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is kind of impossible that your GPU can work at -1V offset voltage. On XTU I get an instant reboot on -500mV&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, despite the apparent physical impossibility of applying such a large voltage offset, there seems to be a real effect on performance. Applying a -500 mV voltage offset resulted in approximately 2 watts lower peak power consumption as measured by &lt;a href="https://github.com/erpalma/throttled"&gt;throttled monitoring&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I benchmarked GPU performance at different voltage offsets to see if they are ignored after a certain point. The result shows diminishing returns beyond 300 millivolt undervoltage. Still, it is not clear whether the larger undervoltage settings are actually being set correctly because it should not be possible for the CPU to operate when undervolted 900 mV.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# config.toml&lt;/span&gt;

&lt;span class="k"&gt;[voltage]&lt;/span&gt;
&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1000&lt;/span&gt;
&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# test.py&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;toml&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;multiprocessing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;tempfile&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NamedTemporaryFile&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;jinja2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;tqdm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tqdm&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;undervolt_gpu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;undervolt___&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.log&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;undervolt_gpu: saving to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;lenovo_fix.conf&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tpl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;NamedTemporaryFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ntf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ntf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ntf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sudo /usr/lib/throttled/lenovo_fix.py --monitor --log &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; --config &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ntf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;voltage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;cmd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;config.toml&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;voltages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;voltage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;voltage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;voltage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;step&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;voltages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;voltage&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tqdm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;voltages&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;undervolt_gpu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;glmark2 -b :duration=2.0 --fullscreen | tee glmark___&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.log&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;process launched&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;process terminating&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;process terminated&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I generated a CSV report by regex parsing the data logged from &lt;code&gt;glmark2&lt;/code&gt; and &lt;code&gt;lenovo_fix.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# report.py&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os.path&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;toml&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;csv&lt;/span&gt;

&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;config.toml&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;voltages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;voltage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;voltage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;voltage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;step&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;undervolting.csv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fieldnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;glmark&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;voltage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;package_watts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;graphics_watts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;dram_watts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeheader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;voltage&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;voltages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;glmark_p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;glmark___&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.log&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;undervolt_p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;undervolt___&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.log&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;glmark_p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;glmark_s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;(?:glmark2 Score: )(\d+)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;undervolt_p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dram&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="s2"&gt;&amp;quot;(?:Package: )(\d*\.?\d+)(?: W - Graphics: )(\d*\.?\d+)(?: W - DRAM: )(\d*\.?\d+)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;groups&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,))()&lt;/span&gt;
                    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;glmark&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;glmark_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;voltage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;package_watts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;graphics_watts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;dram_watts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dram&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;!-- ![](/static/undervolt_v4.svg){:class="img-responsive"} --&gt;
&lt;!-- This GPU benchmark performs best and diminishing returns begin around -400 mV GPU voltage offset. --&gt;

&lt;p&gt;&lt;a href="https://github.com/erpalma/throttled"&gt;throttled&lt;/a&gt; GPU voltage offset adjustment has some effect on GPU performance, but it is unclear to what extent this is meaningful with the Linux implementation. GPU voltage offset adjustment parameters are not on the same scale as those within the Windows-based Intel XTU utility. The relationship between throttled GPU voltage offset and Intel XTU voltage offset warrants further research to fully unlock the battery life and calculation performance potential of our machines.&lt;/p&gt;
&lt;p&gt;To set your GPU voltage offset to -400 mV, edit &lt;code&gt;lenovo_fix.conf&lt;/code&gt; which is located at &lt;code&gt;/etc/lenovo_fix.conf&lt;/code&gt; on Arch Linux if you installed throttled from the &lt;a href="https://www.archlinux.org/packages/community/any/throttled/"&gt;throttled package&lt;/a&gt;.&lt;/p&gt;</content><category term="Software"/><category term="thinkpad"/><category term="undervolt"/><category term="benchmark"/><category term="arch-linux"/></entry></feed>