Any fool can criticize, complain, and condemn— and most fools do. But it takes character and self-control to be understanding and forgiving, Dale Carnegie.
NixOS is a unique, innovative, and powerful Linux distribution that leverages the Nix package manager. Unlike traditional Linux distributions that update packages and system configurations in-place, NixOS uses a purely functional and declarative approach to define the system state, reducing the risk of system breakage.
NixOS is a Linux distribution that uses the Nix package manager to handle packages and system configuration in a purely functional manner. This approach ensures that builds are reproducible and that the system state can be reliably replicated or rolled back.
sudo nix-env ‐‐rollback is a command used to revert your system to the previous configuration. It’s a powerful tool for undoing unintended changes or recovering from failed package installations. Before rolling back, consider if there’s a more targeted solution, like uninstalling specific packages or reverting configuration files.
To install a new operating system, we typically use an ISO image to create a bootable USB drive. We can then plug this USB drive into our device (laptop, desktop computer), boot from that USB and install a new OS.
Traditionally, you were only able to put a single ISO on a USB. So what if you wanted a USB with Microsoft Windows ISO, Arch Linux, TailsOS, Mint, GParted, Clonezilla (a partition and disk imaging/cloning program similar to True Image), and NixOS, is that possible? How could we do that?
Ventoy is an open-source tool that simplifies the creation of bootable USB drives capable of hosting multiple ISO images simultaneously. All we need to do is :format our USB drive with Ventoy, then simply copy multiple ISO files onto this Ventoy-formatted USB drive. Then, we can decide during boot time which ISO Ventoy will load for us.
Install Ventoy temporarily (Ventoy is available without permanently adding it to your system packages): nix-shell -p ventoy-full
Launches a temporary shell environment with the ventoy-full package available.
Launch Ventoy Web Interface. Once inside the nix-shell, execute the command: sudo ventoy-web
[nix-shell:~/myNewPython]$ sudo ventoy-web
# Runs Ventoy's web-based graphical user interface with root privileges, necessary for writing to USB devices.
===============================================================
Ventoy Server 1.0.98 is running ...
Please open your browser and visit http://127.0.0.1:24680
===============================================================
Open your favorite browser and visit the URL provided.
Select the target USB Drive you would like to flash and click the Install button. You can find your USB drive path using lsblk or fdisk -l. Be extremely careful to select the correct drive to avoid overwriting the wrong device and losing all its data.
Check Ventoy Partitions. After running the installation command, your USB drive should have: A small Ventoy partition used by Ventoy (usually around 200MB, e.g., sdc2) and a large data partition where you’ll store your ISO files (e.g., sdc1).
❯ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
[...]
sdc 8:32 0 465,8G 0 disk
├─sdc1 8:33 0 465,7G 0 part /run/media/nmaximo7/Ventoy
└─sdc2 8:34 0 32M 0 part
Add your ISO Files. Mount the data partition (If not already mounted, the data partition (e.g., /run/media/nmaximo7/Ventoy should be accessible). Simply copy or drag and drop your ISO files directly to the USB drive. Ventoy supports multiple ISOs on the same drive. There’s no need to format or create partitions manually.
Hugo is a fast, flexible, and powerful static site generator written in Go. It allows you to create websites with ease by generating HTML files from templates (they define the structure and layout of your web pages. These templates can be customized to fit your design requirements), themes, stylesheets, and content files written in Markdown.
Managing the startup of the Hugo server can be streamlined using NixOS scripts, ensuring that your development environment is consistent, reproducible, and easily maintainable. This section guides you through creating a NixOS script to manage the Hugo server startup within the Kitty terminal emulator, building it using Nix derivations, and integrating it with your shell environment for convenient access.
Step 1. Create the Startup Script. Create a new file withing your dotfiles directory, let’s call it myStartup.nix (~/dotfiles/scripts/myStartup.nix ), with the following content:
# This NixOS script efficiently manages the startup of a Hugo server within the Kitty terminal.
{ pkgs ? import {} }: # Accepts an optional argument `pkgs`,...
# defaulting to importing the Nixpkgs repository.
# Uses writeShellScriptBin. This Nix function creates a packaged script that can be executed as a standalone executable script.
# The script is named myStartup.
pkgs.writeShellScriptBin "myStartup" ''
#!${pkgs.zsh}/bin/zsh # The Shebang specifies the interpreter for this script.
# More specifically, it specifies the script should be executed ...
# using the Zsh shell provided by the Nix package set.
# Variables:
PATH_BLOG=~/justtothepoint # Sets the path to the blog directory (where your Hugo project resides).
SITE_URL="http://localhost:1313" # Defines the URL where the local site (local Hugo server) will be accessible.
# Function to kill any existing Hugo processes to prevent...
# multiple instances from running simultaneously
function kill_blogserv() {
echo "Starting blog" # Inform the user that the blog service is being started.
cd $PATH_BLOG || return 1 # Change to the blog directory; exit if it fails.
${pkgs.procps}/bin/pkill hugo # Kill any running Hugo processes.
}
# Main function to manage the startup process.
function main() {
kill_blogserv # Call the function to stop existing Hugo processes.
# Check if the Kitty terminal is running.
if ${pkgs.procps}/bin/pgrep -x "kitty" > /dev/null
then
echo "Kitty is already running" # Inform the user if Kitty is already running.
else
echo "Kitty is not running. Launching..."
${pkgs.kitty}/bin/kitty & # Start Kitty in the background.
sleep 1 # Wait for a moment to ensure Kitty starts up.
fi
# Start the Hugo server within the Kitty terminal...
# ensuring that the terminal remains open (--hold) after the Hugo server stops
${pkgs.kitty}/bin/kitty --hold sh -c "${pkgs.hugo}/bin/hugo server --disableFastRender --gc --cleanDestinationDir"
}
main # Call the main function to execute the script.
''
Step 2: Make the script executable: chmod +x ~/dotfiles/scripts/myStartup.nix
Step 3. Create a Nix Build Script (build_and_run_myStartup.sh).
In NixOS, a derivation is a data format with three required attributes: name, builder, and system. It is a recipe for how to build a package. We are going to write a building script. It will handle both instantiation and building the derivation while ensuring that is protected from garbage collection.
Let’s create a new file (vim build_and_run_myStartup.sh) and add the following:
#!/bin/sh
# Instantiate the derivation defined in myStartup.nix...
# and create a permanent root reference (--add-root), preventing it from being garbage collected.
DRV_PATH=$(nix-instantiate --add-root /nix/var/nix/gcroots/auto/myStartup.drv ~/dotfiles/scripts/myStartup.nix)
echo "Derivation path: $DRV_PATH"
# Build the derivation,...
# resulting in a path within the Nix store where the executable script resides.
RESULT_PATH=$(nix-build $DRV_PATH)
echo "Result path: $RESULT_PATH"
Step 4. Execute (run) the build script:
✦ ❯ sudo sh ./build_and_run_myStartup.sh
# Example Output:
Derivation path: /nix/var/nix/gcroots/auto/myStartup.drv
Result path: /nix/store/v6waz0jv3vavhyraf88h5h1x7b0lpv3g-myStartup
Step 5: Launch the Startup Script. Execute the built myStartup script to start the Hugo server within Kitty:
nmaximo7 on nixos ~/dotfiles/scripts
✦ ❯ /nix/store/v6waz0jv3vavhyraf88h5h1x7b0lpv3g-myStartup/bin/myStartup
Starting blog
# The script ensures that any existing Hugo process is terminated before starting a new one.
Kitty is already running
# It verifies if the Kitty terminal emulator is running. If not, it launches Kitty.
Hugo Static Site Generator v...
[...]
Web Server is available at http://localhost:1313/
Press Ctrl+C to stop
# It verifies if the Kitty terminal emulator is running. If not, it launches Kitty.
# The Hugo server starts within Kitty, serving your local Hugo site at http://localhost:1313/.
Step 6. Create an alias for the script. To simplify running your startup script, let’s go to our zsh.nix (vim ~/dotfiles/configuration/zsh.nix) and create a shell alias in your Zsh configuration.
{ config, pkgs, ... }:
let
username = config.home.username;
dotDir = "/home/${username}/dotfiles";
in
{
programs.zsh = {
enable = true;
[...]
shellAliases = { # Defines custom aliases for your shell
webserver = "/nix/store/v6waz0jv3vavhyraf88h5h1x7b0lpv3g-myStartup/bin/myStartup";
[... add other aliases ...]
# webserver is an alias that runs the myStartup script
# After rebuilding your NixOS configuration and restarting your shell,...
# you can start the Hugo server by simply typing: webserver
};
};
}
NixOS is a Linux distribution that employs a purely functional package management system called Nix. Nix allows for reproducible builds (each package is described by a derivation, which specifies exactly how it is built and its dependencies) and declarative configurations (system configurations and development environments can be expressed in .nix files and replicated on any Nix-based system), making it ideal for managing development environments.
The nix-shell command is a feature of Nix that provides a way to create on-demand, isolated shell environments with specific dependencies and environmental variables. This is particularly useful for development, as it ensures that all required packages and environment variables are set up consistently across different systems.
1 Step. Create a Directory to Host Your Python Project. This directory will contain all the necessary files for our application: mkdir myPython (it will create a new directory in your current working directory, typically your home directory). The directory will contain: shell.nix (it defines the Nix shell environment and dependencies), your python source files, and requirements.txt (listing your Python dependencies for your Python application).
2 Step. Create a shell.nix File for the Nix Shell Environment. Navigate to the myPython directory and create a shell.nix file. This file defines the environment that nix-shell will set up for you.
# cd myPython, nix-shell
{ pkgs ? import {} }:
# This line defines a function that takes pkgs as an argument,...
# which represents the Nix packages available from the Nix Packages collection (nixpkgs).
# If pkgs is not provided, it defaults (with the ? operator) to importing the package set from your Nix installation.
let
# Declare a set of environment variables
# This is a set of key-value pairs specifying environment variables for your Python application, e.g., NEWS_API_KEY.
envVariables = {
NEWS_API_KEY = "YOUR_NEWS_API";
TIME_PROGRESS = "100";
CITY = "Bangkok";
TIMEZONE = "Asia/Bangkok";
WEBSITE = "https://justtothepoint.com";
WEBSITE_INSECURE = "http://justtothepoint.com";
LOCALWEB = "http://localhost:1313/";
TIME_MONITORING = "20";
KEYBINDING = "~/dotfiles/docs/assets/keybindings.txt";
HUGO_CONTENT_DIR = "~/justtothepoint/content/";
};
# Create a string representation of the .env file content
envFileContent = pkgs.lib.concatStrings (
pkgs.lib.mapAttrsToList (name: value: "${name}=${value}\n") envVariables
# pkgs.lib.mapAttrsToList iterates over each key-value pair in envVariables and formats them as "key=value\n".
# pkgs.lib.concatStrings concatenates the list of strings into a single string (envFileContent).
);
# After defining variables with let, we specify the shell environment using pkgs.mkShell.
in pkgs.mkShell {
# Define build input (packages and dependencies) for the shell environment
buildInputs = [
pkgs.neofetch
pkgs.tk # Add the Tk library
pkgs.xorg.libX11
# X11 libraries, both libraries ensure a typical Python environment that have GUI elements (Tkinter)
pkgs.python311Packages.tkinter
(pkgs.python3.withPackages (python-pkgs: [
# pkgs.python3.withPackages: Produces a Python 3 distribution that includes...
# the specified packages (pip, python-dotenv, and requests) in our shell environment
python-pkgs.pip
python-pkgs.python-dotenv
python-pkgs.requests
]))
];
# The shellHook is a script that runs automatically when you enter the nix-shell. It sets up the development environment by performing several tasks.
shellHook = ''
# This command writes the content of envFileContent to a file named .env, which will be used to load environment variables.
echo "${envFileContent}" > .env
# Create and activate a Python virtual environment
python -m venv .venv # Creates a new virtual environment in the .venv directory.
source .venv/bin/activate # Activates the virtual environment so that installed packages are isolated from the global Python environment.
# Upgrade pip to ensure that the latest version of pip is used within the virtual environment.
pip install --upgrade pip
# Install the Python packages listed in requirements.
pip install -r requirements.txt
# Optionally run a script automatically, e.g., greeting.py
python greeting.py
'';
The shellHook allows you to run commands for performing specific tasks (pip install -r requirements.txt, install the required Python packages in your virtual environment) and scripts automatically when entering the shell, such as setting up a Python virtual environment or generating configuration files (.env).
Before running the script, ensure that you have a requirements.txt file listing all the Python packages your project depends on, e.g., newsapi-python, rich, urllib3, etc.
3 Step. Run nix-shell to launch the Shell Environment. After setting up shell.nix, run the following command from within the myPython directory: nix-shell
The nix-shell command reads the shell.nix file and sets up the environment as specified. This includes installing the packages, setting environment variables, and executing the shellHook. You will be dropped into a new shell where the environment is configured for your Python project, including the virtual environment activation and all necessary dependencies installed.
Step 4. Start coding. Create a Python script named news.py that will fetch the latest news headlines using the News API and that also will leverage the environment variables and dependencies specified in shell.nix: vim news.py.
import os # Provides functions for interacting with the operating system.
from dotenv import load_dotenv # Loads environment variables from a .env file.
from newsapi import NewsApiClient # A client library for the News API that fetches top headlines.
from rich.console import Console # Provides advanced console output capabilities, including colors and formatting.
def fetch_headlines():
"""
Fetches the latest headlines from the News API and prints them.
"""
# Initialize Rich Console for colorful output
console = Console()
# Load environment variables from .env file
load_dotenv()
# Reads the NEWS_API key from .env
# If your environment variables contain sensitive data,...
# avoid committing .env or shell.nix with real credentials to public repositories.
api_key = os.getenv("NEWS_API_KEY")
# Check if API key is available
if not api_key:
console.print("[red]Error:[/red] NEWS_API_KEY is not set.")
return
# Initialize NewsApiClient with the API key
newsapi = NewsApiClient(api_key=api_key)
# Retrieves the top headlines for the specified country, e.g., 'us' for the United States.
top_headlines = newsapi.get_top_headlines(country='us')
# Check if the request was successful
if top_headlines['status'] == 'ok':
articles = top_headlines['articles']
# Extract and print the title of the first 10 headlines...
# using Rich's console printing capabilities to add color formatting.
print("Latest 10 Headlines:")
for i, article in enumerate(articles, start=1):
console.print(str(i) + ". [yellow]" + article['title'] + "[/yellow]. ")
else: # Handle error
print("Failed to fetch headlines:", top_headlines['message'])
# Main Execution Block
if __name__ == "__main__":
# Ensures that fetch_headlines is called only when the script is run directly.
fetch_headlines()
Step 5. Run the code. With the environment set up and the code written, you can now run your script: python news.py.
nix-shell is a command provided by the Nix package manager that allows users to create temporary, isolated development environments. It is particularly useful for running commands or scripts with specific dependencies without permanently installing those packages on your system.
When you enter a nix-shell, it sets up a temporary environment (a ephemeral box) with the specified packages (dependencies) available in your PATH. Once you exit, poof, it’s all gone. No leftover packages cluttering your main system, no messy version conflicts, and no stress whatsoever about messing with your environment.
By default, if you run nix-shell without arguments (if path is not given), nix-shell defaults to a file shell.nix in the current directory.
# Import the Nixpkgs package set, which contains a wide range of packages.
# This allows us to use the packages defined in Nixpkgs in our shell.
{ pkgs ? import {} }:
with (import {});
# Create a shell environment with specified packages and configuration.
mkShell {
# List of packages to be included in the development environment.
buildInputs = [
cowsay # Generates ASCII art with messages
ripgrep # A fast command-line search tool, faster than grep
bat # An enhanced version of 'cat', which includes syntax highlighting
eza # A modern alternative to 'ls' with more features
htop # An interactive process viewer and manager
neofetch # A tool to display system information in a visually appealing way
figlet # Converts text to ASCII art using various fonts
lolcat # A program to colorize text output with rainbow colors
fortune # Displays a random quote or saying
feh # A lightweight image viewer for X11
gedit # A simple and user-friendly text editor
cmatrix # A fun screensaver that displays a scrolling "Matrix" effect
curl # A command-line tool for transferring data with URLs
];
# Shell hook that runs commands automatically when entering the shell.
shellHook = ''
# Use `figlet` to create a large ASCII art greeting and color it with `lolcat`.
echo "Welcome master" | figlet | lolcat
# Display system information using `neofetch`.
neofetch
# Show a random quote or saying using `fortune` piped into cowsay so an ASCCI cow says it.
fortune | cowsay
# Fetch the current weather for Malaga using curl and handle potential errors.
# The -s flag makes curl operate in silent mode (no progress output).
weather=$(${curl}/bin/curl -s 'wttr.in/Malaga?format=3')
if [ $? -eq 0 ]; then
echo "Weather in Malaga: $weather"
else
echo "Failed to fetch weather information"
fi
# Get the current date and time in the specified format.
dt=$(date '+%d/%m/%Y %H:%M:%S');
# Print the current date and time.
echo "$dt"
# Display the current month's calendar.
cal
'';
}