☀️Siang
DevOps & Cloud

NixOS and Nix for Containers

TOKEN

Kuasai NixOS dan Nix untuk reproducible builds, containers, development environments, flakes, dan deployment yang deterministic

Artikel: Nix Containers Artikel: Nix Containers

1. Apa itu Nix?

Nix adalah package manager dan build system yang menjamin reproducible builds. Berbeda dari package manager tradisional (apt, brew, npm), Nix menggunakan pendekatan functional — setiap package di-build di isolated environment dan disimpan di Nix Store dengan hash unik. NixOS adalah distribusi Linux yang dikonfigurasi sepenuhnya menggunakan Nix language.

Nix Ecosystem
📦
Nix Package Manager
Cross-platform
Isolated builds
Rollback support
🐧
NixOS
Declarative OS config
Atomic upgrades
Generations
❄️
Nix Flakes
Pinned dependencies
Reproducible inputs
Standard structure
🐳
Nix Containers
Minimal images
No Dockerfile needed
Nix-built images

1.1 Nix vs Docker

FiturNixDocker
Build SystemFunctional, hash-addressedLayer-based (Dockerfile)
ReproducibilityGuaranteed (content-addressed)Best-effort
SharingBinary cache, deduplicationImage layers
RollbackInstant, atomicRebuild dari image lama
OS ConfigDeclarative (NixOS)Dockerfile (imperative)
Image SizeMinimal (hanya runtime deps)Base image + layers

2. Instalasi Nix

Terminal — Install Nix

# Single-user install (Linux/macOS)
sh <(curl -L https://nixos.org/nix/install) --no-daemon

# Multi-user install (recommended untuk production)
sh <(curl -L https://nixos.org/nix/install) --daemon

# Setelah install, restart shell
# Verifikasi
nix --version

# Aktifkan experimental features (diperlukan untuk flakes)
mkdir -p ~/.config/nix
cat > ~/.config/nix/nix.conf << 'EOF'
experimental-features = nix-command flakes
max-jobs = auto
auto-optimise-store = true
EOF

# Cek Nix store
nix-store --query --requisites /run/current-system | head -20

# Install NixOS (opsional, untuk VM/remote)
# Download ISO dari https://nixos.org/download.html
# Atau gunakan nixos-infect untuk convert VPS ke NixOS
curl -L https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | bash
 ad-slot-wide">

3. Nix Language Basics

Nix language fundamentals

# Nix expression language — functional, lazy, dynamically typed

# Variables dan tipe data
let
  name = "my-app";
  version = "1.0.0";
  debug = false;
  numbers = [ 1 2 3 4 5 ];
  config = {
    host = "localhost";
    port = 8080;
    tls = true;
  };
in {
  inherit name version;  # shorthand untuk name = name; version = version;
}

# Functions
let
  # Function dengan satu parameter
  greet = name: "Hello, ${name}!";

  # Function dengan multiple arguments (currying)
  multiply = a: b: a * b;

  # Function dengan pattern matching
  process = { name, version ? "0.0.1", ... }: "${name}-${version}";
in {
  greeting = greet "World";         # "Hello, World!"
  result = multiply 3 4;            # 12
  package = process { name = "app"; };  # "app-0.0.1"
}

# Let-in expressions
let
  pkgs = import  {};
  python = pkgs.python312;
in
pkgs.mkShell {
  buildInputs = [
    python
    python.pkgs.requests
    python.pkgs.flask
    pkgs.nodejs_20
  ];
}

# Import & Fetch
let
  # Import dari file lokal
  config = import ./config.nix;

  # Fetch dari URL
  src = fetchTarball {
    url = "https://github.com/my-org/my-app/archive/v1.0.tar.gz";
    sha256 = "abc123...";
  };

  # Fetch dari Git
  flake = fetchGit {
    url = "https://github.com/NixOS/nixpkgs.git";
    rev = "abc123def456...";
  };
in
  # ...

4. Nix Flakes

Flakes adalah mekanisme standar untuk mendefinisikan Nix project dengan pinned dependencies. Flakes menggantikan channel-based system dengan file flake.nix yang menentukan semua input secara eksplisit.

flake.nix — Project dengan Nix Flakes

{
  description = "My Node.js application";

  # Input dependencies (semua di-pin)
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
    flake-utils.url = "github:numtide/flake-utils";
    devenv.url = "github:cachix/devenv";
  };

  # Outputs
  outputs = { self, nixpkgs, flake-utils, devenv }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
        nodejs = pkgs.nodejs_20;
      in {
        # Development shell
        devShells.default = pkgs.mkShell {
          buildInputs = [
            nodejs
            pkgs.nodePackages.npm
            pkgs.nodePackages.prettier
            pkgs.docker
            pkgs.kubectl
            pkgs.terraform
          ];
          shellHook = ''
            echo "🚀 Development environment ready!"
            echo "Node.js: $(node --version)"
            echo "npm: $(npm --version)"
          '';
        };

        # Build package
        packages.default = pkgs.buildNpmPackage {
          pname = "my-app";
          version = "1.0.0";
          src = ./.;
          npmDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";

          buildPhase = ''
            npm run build
          '';

          installPhase = ''
            mkdir -p $out/bin
            cp -r dist/* $out/
            makeWrapper ${nodejs}/bin/node $out/bin/my-app \
              --add-flags "$out/server.js"
          '';
        };

        # Docker image
        packages.dockerImage = pkgs.dockerTools.buildLayeredImage {
          name = "my-app";
          tag = "latest";
          contents = [
            self.packages.${system}.default
            pkgs.cacert    # SSL certificates
            pkgs.busybox   # Minimal shell (untuk debug)
          ];
          config = {
            Cmd = [ "/bin/my-app" ];
            ExposedPorts = { "8080/tcp" = {}; };
            Env = [
              "NODE_ENV=production"
              "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
            ];
            Labels = {
              "org.opencontainers.image.source" = "https://github.com/my-org/my-app";
            };
          };
        };
      }
    );
}
Terminal — Flake commands

# Initialize flake di project baru
nix flake init

# Update dependencies
nix flake update

# Update specific input
nix flake update nixpkgs

# Enter development shell
nix develop
# Atau dengan specific shell
nix develop .#default

# Build package
nix build

# Build Docker image
nix build .#dockerImage

# Load ke Docker
docker load < result

# Run
docker run -p 8080:8080 my-app:latest

# Check flake.lock
nix flake metadata

5. Nix Containers

Nix bisa menghasilkan container images yang minimal — hanya berisi runtime dependencies yang benar-benar dibutuhkan. Tidak perlu Dockerfile, semua di-build dari Nix expressions.

Nix container build methods

# Method 1: buildLayeredImage — layered, optimized
packages.dockerImage = pkgs.dockerTools.buildLayeredImage {
  name = "my-app";
  tag = "latest";

  # Contents: packages yang ada di dalam container
  contents = [
    self.packages.${system}.default
    pkgs.cacert
    # JANGAN taruh pkgs.bash atau tools lain
    # kecuali benar-benar diperlukan
  ];

  # Layer yang paling sering berubah di layer paling atas
  maxLayers = 15;

  config = {
    Cmd = [ "/bin/my-app" ];
    ExposedPorts = { "8080/tcp" = {}; };
    User = "nobody";  # Non-root user
  };
};

# Method 2: buildImage — single layer, lebih kecil
packages.dockerImageSmall = pkgs.dockerTools.buildImage {
  name = "my-app-minimal";
  tag = "latest";
  copyToRoot = pkgs.buildEnv {
    name = "image-root";
    paths = [ self.packages.${system}.default pkgs.cacert ];
  };
  config = {
    Cmd = [ "/bin/my-app" ];
  };
};

# Method 3: streamLayeredImage — stream langsung ke docker
# Tidak menyimpan file ke disk
packages.dockerStream = pkgs.dockerTools.streamLayeredImage {
  name = "my-app";
  tag = "latest";
  contents = [ self.packages.${system}.default ];
  config.Cmd = [ "/bin/my-app" ];
};

# Build & load ke Docker
# nix build .#dockerImage
# docker load < result

# Atau stream langsung
# nix build .#dockerStream | docker load

# Perbandingan ukuran image
# Nix layered image: ~30-50MB (Go app)
# Dockerfile (alpine): ~50-80MB
# Dockerfile (debian): ~150-300MB
💡 Mengapa Nix Container Lebih Kecil?

Nix secara otomatis hanya memasukkan runtime dependencies yang benar-benar diperlukan. Tidak ada base image (Alpine/Debian) yang menyertakan tools yang tidak perlu. Untuk aplikasi Go, image Nix bisa hanya 20-30MB karena hanya berisi binary dan minimal runtime dependencies.

6. devenv — Development Environments

devenv (powered by Nix) adalah tool untuk membuat development environments yang reproducible. Seperti Docker Compose untuk development, tapi lebih cepat dan native.

devenv.nix — Development environment

# Install devenv
nix profile install nixpkgs#devenv

# Init di project baru
devenv init

# devenv.nix
{ pkgs, ... }:
{
  # Packages yang tersedia di shell
  packages = [
    pkgs.git
    pkgs.ripgrep
    pkgs.jq
    pkgs.curl
  ];

  # Languages
  languages = {
    javascript = {
      enable = true;
      package = pkgs.nodejs_20;
      npm.enable = true;
    };
    typescript.enable = true;
    python = {
      enable = true;
      version = "3.12";
      venv.enable = true;
      venv.requirements = ./requirements.txt;
    };
    go.enable = true;
  };

  # Services (berjalan otomatis saat enter shell)
  services = {
    postgres = {
      enable = true;
      initialDatabases = [
        { name = "myapp"; }
      ];
      listen_addresses = "127.0.0.1";
      port = 5432;
    };
    redis.enable = true;
  };

  # Environment variables
  env = {
    DATABASE_URL = "postgresql://localhost:5432/myapp";
    REDIS_URL = "redis://localhost:6379";
    NODE_ENV = "development";
  };

  # Scripts yang bisa dijalankan
  scripts = {
    dev.exec = "npm run dev";
    test.exec = "npm test";
    lint.exec = "npm run lint";
    db-reset.exec = ''
      psql -h localhost -U postgres -c "DROP DATABASE IF EXISTS myapp;"
      psql -h localhost -U postgres -c "CREATE DATABASE myapp;"
    '';
  };

  # Pre-commit hooks
  pre-commit.hooks = {
    nixpkgs-fmt.enable = true;
    eslint.enable = true;
    prettier.enable = true;
  };

  # Enter shell
  enterShell = ''
    echo "🚀 Development environment ready!"
    echo "Node: $(node --version)"
    echo "Python: $(python --version)"
    echo ""
    echo "Commands: dev, test, lint, db-reset"
  '';
}

7. deploy-rs — Deployment

deploy-rs adalah tool untuk deploy NixOS configurations ke remote servers. Seperti Ansible/Terraform untuk NixOS, tapi lebih cepat dan atomic.

deploy-rs configuration

# Tambahkan deploy-rs ke flake inputs
inputs = {
  nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
  deploy-rs.url = "github:serokell/deploy-rs";
};

# Definisikan deployment targets
outputs = { self, nixpkgs, deploy-rs, ... }: {
  # NixOS configurations
  nixosConfigurations.web-server = nixpkgs.lib.nixosSystem {
    system = "x86_64-linux";
    modules = [
      ./hosts/web-server/configuration.nix
    ];
  };

  nixosConfigurations.db-server = nixpkgs.lib.nixosSystem {
    system = "x86_64-linux";
    modules = [
      ./hosts/db-server/configuration.nix
    ];
  };

  # Deploy configuration
  deploy.nodes = {
    web-server = {
      hostname = "web.example.com";
      sshUser = "deploy";
      # profiles default akan deploy system configuration
      profiles.system = {
        user = "root";
        path = deploy-rs.lib.x86_64-linux.activate.nixos
          self.nixosConfigurations.web-server;
      };
    };

    db-server = {
      hostname = "db.example.com";
      sshUser = "deploy";
      profiles.system = {
        user = "root";
        path = deploy-rs.lib.x86_64-linux.activate.nixos
          self.nosConfigurations.db-server;
        # Rollback timeout — auto rollback jika deploy gagal
        magicRollback = true;
      };
    };
  };
};

# Deploy commands
# Deploy semua nodes
deploy .

# Deploy specific node
deploy .#web-server

# Dry-run (hanya preview)
deploy .#web-server --dry-activate

# Rollback
ssh deploy@web.example.com "sudo /nix/var/nix/profiles/system-*-link/bin/switch-to-configuration switch"

8. NixOS Configuration

configuration.nix — NixOS server config

# hosts/web-server/configuration.nix
{ config, pkgs, ... }:
{
  # System basics
  system.stateVersion = "24.05";
  networking.hostName = "web-server";
  time.timeZone = "Asia/Jakarta";

  # Enable SSH
  services.openssh = {
    enable = true;
    settings = {
      PasswordAuthentication = false;
      PermitRootLogin = "no";
    };
  };

  # Firewall
  networking.firewall = {
    enable = true;
    allowedTCPPorts = [ 22 80 443 ];
  };

  # Nginx reverse proxy
  services.nginx = {
    enable = true;
    recommendedProxySettings = true;
    recommendedTlsSettings = true;

    virtualHosts."app.example.com" = {
      enableACME = true;
      forceSSL = true;
      locations."/" = {
        proxyPass = "http://127.0.0.1:8080";
        proxyWebsockets = true;
      };
    };
  };

  # Let's Encrypt
  security.acme = {
    acceptTerms = true;
    certs."app.example.com".email = "admin@example.com";
  };

  # Application service
  systemd.services.my-app = {
    description = "My Node.js Application";
    after = [ "network.target" ];
    wantedBy = [ "multi-user.target" ];
    serviceConfig = {
      ExecStart = "${pkgs.nodejs_20}/bin/node /opt/my-app/server.js";
      Restart = "always";
      User = "myapp";
      Group = "myapp";
      Environment = [
        "NODE_ENV=production"
        "PORT=8080"
      ];
    };
  };

  # Users
  users.users.myapp = {
    isSystemUser = true;
    group = "myapp";
  };
  users.groups.myapp = {};

  # Monitoring
  services.prometheus.exporters.node = {
    enable = true;
    port = 9100;
  };
}
⚠️ NixOS Learning Curve

NixOS memiliki learning curve yang cukup tinggi. Bahasa Nix berbeda dari bahasa pemrograman umum — bersifat fungsional, lazy evaluation, dan tidak banyak error messages yang jelas. Namun begitu dipahami, NixOS menawarkan tingkat reproducibility dan atomic rollback yang tidak ditawarkan oleh tool lain.

9. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut:

Pertanyaan 1: Apa yang membuat Nix builds reproducible?

a) Menggunakan Docker containers
b) Setiap package di-build di isolated environment dengan hash unik berdasarkan semua input, sehingga hasilnya selalu sama
c) Menggunakan base image yang sama
d) Menggunakan lockfile seperti package-lock.json

Pertanyaan 2: Apa fungsi Nix Flakes?

a) Menggantikan Docker
b) Mendefinisikan Nix project dengan pinned dependencies secara eksplisit, menggantikan channel system
c) Membuat NixOS installation media
d) Mengelola Git repository

Pertanyaan 3: Mengapa Nix container images lebih kecil dari Dockerfile-based images?

a) Karena Nix menggunakan kompresi yang lebih baik
b) Karena Nix hanya memasukkan runtime dependencies yang benar-benar diperlukan tanpa base image
c) Karena Nix menghapus file yang tidak diperlukan
d) Karena Nix containers tidak mendukung multi-layer

Pertanyaan 4: Apa keunggulan deploy-rs dibandingkan SSH + script biasa?

a) deploy-rs lebih cepat dari SSH
b) deploy-rs mendukung atomic deployment dengan automatic rollback jika deploy gagal
c) deploy-rs menggantikan SSH sepenuhnya
d) deploy-rs hanya untuk NixOS desktop

Pertanyaan 5: Apa yang dimaksud dengan atomic upgrade di NixOS?

a) Upgrade yang membutuhkan reboot
b) Sistem beralih ke generasi baru secara instant — jika gagal, bisa rollback ke generasi sebelumnya tanpa downtime
c) Upgrade yang dilakukan secara otomatis
d) Upgrade yang hanya mempengaruhi satu package
← SebelumnyaGrafana Stack: Mimir + Tempo + Loki Selanjutnya →Chaos Engineering with Litmus
🔍 Zoom
100%
🎨 Tema