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.
Isolated builds
Rollback support
Atomic upgrades
Generations
Reproducible inputs
Standard structure
No Dockerfile needed
Nix-built images
1.1 Nix vs Docker
| Fitur | Nix | Docker |
|---|---|---|
| Build System | Functional, hash-addressed | Layer-based (Dockerfile) |
| Reproducibility | Guaranteed (content-addressed) | Best-effort |
| Sharing | Binary cache, deduplication | Image layers |
| Rollback | Instant, atomic | Rebuild dari image lama |
| OS Config | Declarative (NixOS) | Dockerfile (imperative) |
| Image Size | Minimal (hanya runtime deps) | Base image + layers |
2. Instalasi 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
3. Nix Language Basics
# 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.
{
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";
};
};
};
}
);
}
# 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.
# 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
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.
# 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.
# 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
# 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 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: