Wednesday, May 27, 2026

GPG, PGP and SSH

All these terms relate to asymmetric (public-key) cryptography, a system where you use two mathematically linked keys: a public key that you share with everyone and a private key that you keep secret.

1. Public Key (The Basics)

Think of a public key like a padlock that you leave wide open for anyone to use, and the private key as the physical key only you have.

Encryption: Anyone can use your public key to lock (encrypt) a message so that only you can unlock (decrypt) it with your private key.
Signing: You can "sign" a file with your private key to prove it came from you. Others use your public key to verify that the signature is valid and the file hasn't been tampered with.

2. PGP vs. GPG

These two are often used interchangeably because they do the same thing: encrypt and sign data.

PGP (Pretty Good Privacy): The original encryption program created in 1991. It is now a proprietary/commercial product owned by Symantec.
GPG (GNU Privacy Guard): A free, open-source version of PGP. It follows the "OpenPGP" standard, making it compatible with PGP.
Primary Use: Securing emails, signing software packages, and encrypting files at rest.

3. SSH (Secure Shell)

While PGP/GPG is mostly for securing data, SSH is a protocol specifically for securing connections between computers.

How it works: You put your SSH public key on a remote server (like a GitHub account or a Linux VPS). When you try to log in, the server uses that public key to challenge your computer. Your computer "proves" its identity using your matching private key.
Primary Use: Logging into servers remotely or pushing code to repositories without typing a password every time.

Key Differences at a Glance

Feature GPG / PGP SSH
Main Goal Protecting data (emails, files) Protecting access (logging into servers)
Trust Model Web of Trust: Users sign each other's keys to verify identity Trust on First Use: You manually approve the server's key the first time you connect
Typical Format Often looks like a block of text starting with
-----BEGIN PGP PUBLIC KEY BLOCK-----
Often a single line starting with
ssh-rsa or ssh-ed25519

Pro Tip

You can actually use a GPG key for SSH authentication by using a GPG Agent, which lets you manage all your security needs with a single master key.

Tuesday, May 26, 2026

Docker Compose Command Reference

Command Description Key Flags Targets Single/Selective Services? How to Target Works with Profiles?
docker compose up Starts your application -d (background), --build (rebuild images) Yes docker compose up <service> Yes, activates profile services
docker compose down Stops and removes containers -v (delete volumes) No (always stops everything) N/A No (stops all active profiles)
docker compose stop Freezes containers without deleting None Yes docker compose stop <service> Yes
docker compose start Re-activates stopped containers None Yes docker compose start <service> Yes
docker compose restart Restarts running/stopped containers None Yes docker compose restart <service> Yes
docker compose ps Shows current container status --all (shows stopped containers) Yes docker compose ps <service> Yes
docker compose logs Views application output -f (live stream) Yes docker compose logs <service> Yes
docker compose exec Runs a command inside a container Requires service name and command Required (always targets one) docker compose exec <service> <cmd> Yes
docker compose build Compiles project images --no-cache (fresh build) Yes docker compose build <service> Yes
docker compose pull Downloads updated registry images None Yes docker compose pull <service> Yes
docker compose config Validates YAML file syntax None No (validates whole file) N/A No


A note about docker compose profiles


Profiles let you define services that only start when explicitly requested (e.g., debugging tools or frontend dev servers).

YAML
services:
  web:
    image: nginx
    # Starts by default

  db-admin:
    image: phpmyadmin
    profiles:
      - debug
    # Only starts if the "debug" profile is called

To run the default services plus the profile services, use the --profile flag:

Bash
docker compose --profile debug up

Example Configuration : 

Below is an example of a docker compose file which uses profiles and "depends_on" section to manage dependency (e.g. backend will wait till DB is healthy) and the use of "--profile"  option to mange which services to create.
YAML
services:
  database:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: app_db
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    # Starts automatically because 'backend' depends on it

  backend:
    image: node:20-alpine
    command: npm start
    depends_on:
      database:
        condition: service_healthy 
        # Backend waits until database passes its healthcheck

  frontend:
    image: nginx:alpine
    profiles:
      - UI
    # Kept hidden unless the 'UI' profile is explicitly activated

  db-admin:
    image: dpage/pgadmin4
    profiles:
      - debug
    # Kept hidden unless the 'debug' profile is explicitly activated

Quick Commands for This File

Start Core Backend Only: Runs database first, then backend.
Bash
docker compose up -d backend
Start Full Stack (with UI): Activates the frontend service.
Bash
docker compose --profile UI up -d
Run Debug Tools Only: Starts just the administration panel.
Bash
docker compose --profile debug up -d db-admin

Docker Builders

In Docker, a builder is a BuildKit daemon that executes the instructions in your Dockerfile to produce a container image. While the standard Docker Engine comes with a built-in builder, modern development often uses Docker Buildx to manage multiple builder instances with different "drivers" for advanced features like multi-platform builds and remote caching.

Core Builder Drivers

docker driver

The default driver that uses the BuildKit library bundled into your local Docker Engine.

docker-container driver

Spawns a dedicated BuildKit instance inside a container. This is required for multi-platform builds (e.g., building ARM64 images on an AMD64 machine).

kubernetes driver

Runs builds as pods within a Kubernetes cluster, allowing you to scale your build infrastructure.

remote driver

Connects to a manually managed, remote BuildKit daemon, which is useful for specialized CI/CD setups.

Advanced Solutions

For teams needing more speed or specialized environments, several managed builder options exist:

  • Docker Build Cloud: A managed service that offloads builds to high-performance cloud infrastructure to speed up local and CI pipelines.
  • Google Cloud Build: Uses prebuilt Docker images (cloud builders) to execute tasks within a serverless pipeline.
  • Packer Docker Builder: A tool from HashiCorp that builds Docker images without using Dockerfiles, often by running provisioners inside a container and then committing the result.

Essential Commands

You can manage your builders via the Buildx CLI:

docker buildx ls

List all available builders.

docker buildx create --use

Create and switch to a new builder instance.

docker buildx inspect

View details about a specific builder's capabilities

Summary of Docker Builders

Builder / Driver Best Used For Pros Cons
docker (Default) Standard local Linux development. • Zero setup.
• Uses local daemon storage.
• No multi-platform builds.
• Limited advanced caching.
desktop-linux Docker Desktop users (Mac/Win/Linux). • Ready out-of-the-box.
• Built-in QEMU emulation.
• Tied to VM resource limits.
• Slow emulated builds.
docker-container Advanced local caching & multi-platform. • Isolated sandbox.
• Concurrent builds.
• Requires quick initial setup.
• Images not auto-loaded to host.
kubernetes Enterprise CI/CD pipelines. • Scales automatically.
• Offloads heavy host builds.
• Cluster management overhead.
• Complex RBAC setup.
remote Centralised team build servers. • Saves local host resources.
• Centralised team caching.
• Manual setup required.
• Requires TLS certificate management.
Cloud Services (Docker Build Cloud / Depot) Fast multi-architecture team builds. • Native ARM/AMD hardware.
• Instant shared caching.
• Requires paid subscriptions.
• Needs internet connectivity.

Monday, May 25, 2026

LLM Quantizations

Quantization in Large Language Models (LLMs) is a compression technique that reduces a model's memory footprint and computational requirements by converting its numerical values (weights and activations) from high precision to lower precision. It acts like resizing a massive image into a smaller file while preserving most of its quality.

Why is Quantization Used?

  • Memory Reduction: Storing models requires significant VRAM. Converting a model from 16-bit floating-point (FP16) to 4-bit integers (INT4) can reduce the model's size by up to 75%, often fitting massive models onto standard consumer hardware.
  • Faster Inference: Lower precision allows the processor to perform math much faster, resulting in quicker response times.
  • Accessibility: It allows developers and researchers to run capable AI models locally on laptops or less expensive hardware.

Common Data Formats

LLMs are usually trained using high precision formats like FP32 or FP16. Quantization maps these values to lower precision formats:

  • FP16 / BF16 (16-bit): Standard sizes where parameters occupy 2 bytes of memory.
  • INT8 (8-bit): Parameters occupy 1 byte.
  • INT4 (4-bit): Parameters occupy half a byte. This yields the highest compression but introduces a slight risk of losing accuracy.

How it Works

At its core, quantization maps a broad, continuous range of floating-point numbers into a smaller, discrete set of numbers. For example, instead of storing the exact value 0.123456789 (which takes up a lot of memory), the model rounds and stores an approximate, lower-precision number.

Two main approaches are used to achieve this:

  • Weight-Only Quantization: The model's static weights are converted to a lower precision format to save space. During generation, they are temporarily converted back to high precision to compute the response.
  • Weight and Activation Quantization: Both the weights and the dynamic calculations occurring as the model processes text are quantized, which provides faster speeds but requires specialized software support.

Popular Quantization Methods

Several advanced algorithms help maintain model intelligence during compression:

  • GGUF - GPT Generated Unified Format (formerly GGML - GPT-Generated Model Language): A file format widely used in desktop and local hardware applications that allows you to offload parts of the model onto a standard CPU.
  • GPTQ - Generative Post-Training Quantization:  A highly efficient method that compresses weights down to 3 or 4 bits, minimizing accuracy loss.
  • AWQ: (Activation-aware Weight Quantization) A technique that focuses on preserving the most important weights (those that activate during processing), allowing for excellent quality retention.

To explore and utilize these different quantization formats for local deployment, you can check out community-driven repositories such as Hugging Face Models to find optimized, ready-to-run versions of popular LLMs.

Summary of Quantization Algorithms

Precision Algorithm / Format Core Mathematical Approach Best Used For
4-Bit Q4_K_M (Quantization 4 bit, K-Quant, Medium) Block-wise mixed linear quantization GGUF/llama.cpp CPU & Mac inference
IQ4_NL (Importance Quantization 4 bit, Non Linear)  Non-linear grid mapping via Importance Matrix Maximizing accuracy in small 4-bit models
GPTQ (Generative Post Training Quantization) Second-order optimization using Hessian matrices Fast, static GPU inference
AWQ (Activation Aware Weight Quantization)  Activation-aware scaling protecting top 1% weights High-throughput GPU serving (vLLM)
NF4 (Normal float 4 bit)  Quantile distribution for normally distributed data Resource-efficient QLoRA fine-tuning
SpQR (Sparse Quantized Representation)  Outlier isolation (FP16) + base weight compression Extreme accuracy retention at low bits
QuIP / QuIP-Sharp (Quantization with Incoherence Processing) Random orthogonal transformations to smooth outliers Highly stable ultra-low bit pushes
8-Bit LLM.int8() Vector-wise separation of extreme outlier channels Standard zero-shot Hugging Face loading
SmoothQuant Mathematical migration of difficulty from activation to weight Fast INT8 matrix multiplication on GPUs
Q8_0 Uniform, symmetric block-wise linear quantization Baseline GGUF inference with near-zero loss
FP8 (E4M3 / E5M2)
* E :  Exponent
* M :  Mantissa
Dynamic floating-point exponent/mantissa splitting Native hardware acceleration (H100/Blackwell)
16-Bit FP16 (Floating Point 16 bit) 1 sign, 5 exponent, 10 mantissa down-casting Standard half-precision consumer GPU inference
BF16 (Brain Floating Point 16 bit)  1 sign, 8 exponent, 7 mantissa down-casting Training and inference without overflow risks

dockerd, containerd and runc

While Docker was originally a monolithic application, it has evolved into a modular system where containerd serves as the core industry-standard container runtime responsible for managing the complete lifecycle of containers.

Architecture of Docker

Docker follows a client-server architecture. The main components interact to build, run, and distribute containers:

  • Docker Client: The primary way users interact with Docker. When you run commands like docker run, the client sends them to the Docker Daemon via a REST API.
  • Docker Daemon (dockerd): A background process that manages Docker objects such as images, containers, networks, and volumes. It receives requests from the client and handles the high-level logic.
  • containerd: When the daemon needs to run a container, it hands off the request to containerd. Containerd handles low-level operations like image pulling, storage management, and container execution.
  • runc: This is the lowest-level component. Containerd uses runc to interface directly with the Linux kernel (using features like namespaces and cgroups) to create the actual isolated container process.
  • Docker Registry: A storage system for Docker images, such as the official Docker Hub. The daemon pulls images from the registry when they aren't available locally.

Workflow Example

When you execute docker run:

  1. The Client sends the command to dockerd.
  2. dockerd pulls the image from the Registry if it's not local.
  3. dockerd instructs containerd to prepare the container environment.
  4. containerd uses runc to start the container on the host operating system.

Redis Topologies

Topology Best Used For Complexity Writes Scalable?
Standalone (e.g. apt setup) Local development / testing. Very Low No
Leader-Follower (Our next step) Small/Medium apps needing read-scalability. Medium No (Only 1 Leader)
Sentinel High Availability (Auto-failover) for Leader-Follower. High No (Only 1 Leader)
Cluster Mode Massive datasets that don't fit on one machine. High Yes (Shards data across masters)
Active-Passive Disaster Recovery across different cloud regions. Very High No (Only Main Region)
Active-Active Global apps requiring ultra-low latency worldwide. Extremely High Yes (Write anywhere globally)

Understanding the React ESLint Warning: “Avoid Calling setState() Directly Within an Effect”

While working with React, I encountered an interesting ESLint warning related to useEffect.

The application itself was running perfectly fine, but ESLint still flagged the code with an error.

At first glance, the warning looked confusing because useEffect is supposed to handle side effects like fetching data.

This article explains:

  • what the warning actually means,
  • why ESLint complained,
  • why the corrected version passed,
  • and the deeper React concepts involved.

The Original Code


// Helper function to fetch messages from the backend API
const fetchEntries = async () => {
  try {
    const res = await fetch(API_URL);

    if (!res.ok) {
      throw new Error('Backend failed to respond');
    }

    const data = await res.json();

    setEntries(data);
    setError(null);

  } catch (err) {
    setError('Could not fetch guestbook entries. Is the backend running?');
    console.error(err);
  }
};

// Fetch entries automatically when the page loads
useEffect(() => {
  fetchEntries();
}, []);

  

The ESLint Error

ESLint produced the following warning:

Error: Calling setState synchronously within an effect can trigger cascading renders

Avoid calling setState() directly within an effect

This warning came from:

react-hooks/set-state-in-effect

The Corrected Version

The corrected version moved the async function inside the effect.


useEffect(() => {

  const fetchEntries = async () => {
    try {
      const res = await fetch(API_URL);

      if (!res.ok) {
        throw new Error('Backend failed to respond');
      }

      const data = await res.json();

      setEntries(data);
      setError(null);

    } catch (err) {
      setError('Could not fetch guestbook entries. Is the backend running?');
      console.error(err);
    }
  };

  fetchEntries();

}, []);

  

Interestingly, this version passed ESLint successfully.

Was the Original Code Actually Wrong?

Not really.

Both versions are almost identical in runtime behavior.

The application worked because the state update was not actually synchronous.

The important line is:

await fetch(API_URL);

That await creates an asynchronous boundary.

So the sequence is actually:

useEffect runs

fetch request starts

effect completes

network response arrives later

setState executes

This is very different from:


useEffect(() => {
  setCount(count + 1);
}, []);

  

where the state update happens immediately during the effect execution.

Then Why Did ESLint Complain?

The answer lies in static analysis.

ESLint does not execute the program like a browser does. Instead, it analyzes the source code structure and tries to detect potentially dangerous patterns.

In the original code, ESLint saw:


useEffect(() => {
  fetchEntries();
}, []);

  

and noticed that:

fetchEntries()

contains:

  • setEntries(...)
  • setError(...)

From ESLint’s perspective:

Effect → function call → setState

So it assumed:

“This effect may be synchronously updating state.”

The linter could not safely prove that the state updates only happen after an asynchronous operation.


The complete error as received was: 

error  Error: Calling setState synchronously within an effect can trigger cascading renders


Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:

* Update external systems with the latest state from React.

* Subscribe for updates from some external system, calling setState in a callback function when external state changes.


Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

GPG, PGP and SSH

All these terms relate to asymmetric (public-key) cryptography, a system where you use two mathematically linked keys: a public...