diff --git a/hosts/lio/containers_test.nix b/hosts/lio/containers_test.nix new file mode 100644 index 0000000..5e398cc --- /dev/null +++ b/hosts/lio/containers_test.nix @@ -0,0 +1,213 @@ +{ + config, + pkgs, + ... +}: +{ + +# NOTE some useful links +# nixos containers: https://blog.beardhatcode.be/2020/12/Declarative-Nixos-Containers.html +# https://nixos.wiki/wiki/NixOS_Containers +# + + options.services.librechat = + let + lib = pkgs.lib; + in + { + enable = lib.mkEnableOption "LibreChat service"; + port = lib.mkOption { + type = lib.types.port; + default = 3080; + description = "Port number for the LibreChat API service"; + }; + ragPort = lib.mkOption { + type = lib.types.port; + default = 8000; + description = "Port number for the RAG API service"; + }; + dataDir = lib.mkOption { + type = lib.types.path; + default = "/var/lib/librechat"; + description = "Directory to store LibreChat data"; + }; + }; + + config = { + ## Give internet access + # networking.nat.enable = true; + # networking.nat.internalInterfaces = [ "ve-*" ]; + # networking.nat.externalInterface = "eth0"; + + # Random test + containers.wasabi = { + ephemeral = true; + autoStart = true; + privateNetwork = true; + hostAddress = "192.168.100.2"; + localAddress = "192.168.100.11"; + config = + { config, pkgs, ... }: + { + system.stateVersion = "24.11"; + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall = { + enable = true; + allowedTCPPorts = [ 80 ]; + }; + }; + }; + + virtualisation.oci-containers = { + backend = "docker"; # or "podman" + containers = { + # Example of defining a container from the compose file + "test_nginx" = { + # autoStart = true; this is default true + image = "nginx:latest"; + ports = [ + "127.0.0.1:8085:80" + ]; + }; + + # librechat + librechat = { + image = "ghcr.io/danny-avila/librechat-dev:latest"; + ports = [ + "${toString config.services.librechat.port}:${toString config.services.librechat.port}" + ]; + dependsOn = [ + "librechat_mongodb" + "librechat_rag_api" + ]; + environment = { + HOST = "0.0.0.0"; + MONGO_URI = "mongodb://librechat_mongodb:27017/LibreChat"; + MEILI_HOST = "http://librechat_meilisearch:7700"; + RAG_PORT = toString config.services.librechat.ragPort; + RAG_API_URL = "http://librechat_rag_api:${toString config.services.librechat.ragPort}"; + }; + environmentFiles = [ "${config.services.librechat.dataDir}/.env" ]; + volumes = [ + "${config.services.librechat.dataDir}/.env:/app/.env" + "${config.services.librechat.dataDir}/librechat.yaml:/app/librechat.yaml" + "${config.services.librechat.dataDir}/images:/app/client/public/images" + "${config.services.librechat.dataDir}/logs:/app/api/logs" + ]; + extraOptions = [ "--network=librechat-network" ]; + }; + + librechat_mongodb = { + image = "mongo"; + volumes = [ + "${config.services.librechat.dataDir}/data-node:/data/db" + ]; + cmd = [ + "mongod" + "--noauth" + ]; + extraOptions = [ "--network=librechat-network" ]; + }; + + librechat_meilisearch = { + image = "getmeili/librechat_meilisearch:v1.7.3"; + environment = { + MEILI_HOST = "http://librechat_meilisearch:7700"; + MEILI_NO_ANALYTICS = "true"; + }; + volumes = [ + "${config.services.librechat.dataDir}/meili_data_v1.7:/meili_data" + ]; + extraOptions = [ "--network=librechat-network" ]; + }; + + librechat_vectordb = { + image = "ankane/pgvector:latest"; + environment = { + POSTGRES_DB = "mydatabase"; + POSTGRES_USER = "myuser"; + POSTGRES_PASSWORD = "mypassword"; + }; + volumes = [ + "${config.services.librechat.dataDir}/pgdata2:/var/lib/postgresql/data" + ]; + extraOptions = [ "--network=librechat-network" ]; + }; + + librechat_rag_api = { + image = "ghcr.io/danny-avila/librechat-rag-api-dev-lite:latest"; + environment = { + DB_HOST = "librechat_vectordb"; + RAG_PORT = toString config.services.librechat.ragPort; + OPENAI_API_KEY = "not_using_openai"; + }; + dependsOn = [ "librechat_vectordb" ]; + environmentFiles = [ "${config.services.librechat.dataDir}/.env" ]; + extraOptions = [ "--network=librechat-network" ]; + }; + + # TODO revisit local whisper, for now I am using groq free for STT + # librechat_whisper = { + # image = "onerahmet/openai-whisper-asr-webservice:latest"; + # # ports = [ "8080:8080" ]; + # environment = { + # ASR_MODEL = "base"; # You can change to small, medium, large, etc. + # ASR_ENGINE = "openai_whisper"; + # }; + # extraOptions = [ "--network=librechat-network" ]; + # }; + }; + }; + + systemd.services.create-librechat-network = { + description = "Create Docker network for LibreChat"; + serviceConfig.Type = "oneshot"; + wantedBy = [ "multi-user.target" ]; + script = '' + if ! ${pkgs.docker}/bin/docker network inspect librechat-network >/dev/null 2>&1; then + ${pkgs.docker}/bin/docker network create librechat-network + fi + ''; + }; + + security.acme.acceptTerms = true; + security.acme.email = "admin@joshuabell.xyz"; + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + virtualHosts = { + "local.belljm.com" = { + # enableACME = true; + # forceSSL = true; + locations."/".proxyPass = "http://${config.containers.wasabi.localAddress}:80"; + }; + "127.0.0.1" = { + locations."/wasabi/" = { + extraConfig = '' + rewrite ^/wasabi/(.*) /$1 break; + ''; + proxyPass = "http://${config.containers.wasabi.localAddress}:80/"; + }; + locations."/" = { + return = "404"; # or 444 for drop + }; + }; + "_" = { + default = true; + locations."/" = { + return = "404"; # or 444 for drop + }; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + }; +} diff --git a/hosts/lio/flake.lock b/hosts/lio/flake.lock index b5959f7..ddb1d14 100644 --- a/hosts/lio/flake.lock +++ b/hosts/lio/flake.lock @@ -39,11 +39,11 @@ "rust-overlay": "rust-overlay_2" }, "locked": { - "lastModified": 1735781836, - "narHash": "sha256-3QBrsbyM1DyyXruthYJVAiK7kijJP4Mx996q1NC5FWE=", + "lastModified": 1735868220, + "narHash": "sha256-/2fV5/nf+v/7IY3N/hvVmnfwDmLbqRLNqKTB0954EW0=", "owner": "lilyinstarlight", "repo": "nixos-cosmic", - "rev": "553e7a4b77c4ddf8ed700776f9d71982a14e23c4", + "rev": "ee7f797d293e1956e3df90b748d9992dbb3c82ad", "type": "github" }, "original": { @@ -194,11 +194,11 @@ "ragenix": "ragenix" }, "locked": { - "lastModified": 1735795919, - "narHash": "sha256-vreq5NKH6dCj9nAsR59KTHfT+i9SLDbtGbsEcv0Heuw=", + "lastModified": 1735934181, + "narHash": "sha256-FuIaDsoyBOU8L0842gU9VKGs2wQ3ATRXAZe8LY/HEtY=", "ref": "mod_common", - "rev": "b693858091a4a1e1135393b941ad16cbf21fa5fe", - "revCount": 1, + "rev": "f10210d958d9d27bbd61fda8b72fe10106a1b8c0", + "revCount": 2, "type": "git", "url": "https://git.joshuabell.xyz/dotfiles" }, @@ -339,16 +339,15 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1735697839, - "narHash": "sha256-0Acw0UaLi+VNThsmeX8zOKi000DFrYXNnrgpOpk2+MM=", + "lastModified": 1735854365, + "narHash": "sha256-pNb03vdsQmn0jS5dKAdx2DFZ2QH4RRvrIzZxqpfMcS8=", "owner": "nixos", "repo": "nixpkgs", - "rev": "5eaa5fdf06d2b15d373b82c0f3a1ec1c6cab02ae", + "rev": "cd9f495ef7584a714938915d3fe9327c2735d7e4", "type": "github" }, "original": { "owner": "nixos", - "ref": "master", "repo": "nixpkgs", "type": "github" } @@ -1385,11 +1384,11 @@ "rust-overlay": "rust-overlay_4" }, "locked": { - "lastModified": 1735841437, - "narHash": "sha256-ZwmlaFhOlQ7f6Rq6VxRup7giPiwQlwe71HcoO/laRJo=", + "lastModified": 1735857201, + "narHash": "sha256-zyljmBv1FegF4kF2ZWdSdBCIktSHxJljPipwLOOyjrk=", "ref": "refs/heads/master", - "rev": "71d82c875fff85ae250804f45f1acf65f42cdc1e", - "revCount": 253, + "rev": "31220281739c7b6432f3533313a0fa0164f232c0", + "revCount": 254, "type": "git", "url": "https://git.joshuabell.xyz/nvim" }, @@ -1429,11 +1428,11 @@ ] }, "locked": { - "lastModified": 1735698720, - "narHash": "sha256-+skLL6mq/T7s6J5YmSp89ivQOHBPQ40GEU2n8yqp6bs=", + "lastModified": 1735784864, + "narHash": "sha256-tIl5p3ueaPw7T5T1UXkLc8ISMk6Y8CI/D/rd0msf73I=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "a00807363a8a6cae6c3fa84ff494bf9d96333674", + "rev": "04d5f1836721461b256ec452883362c5edc5288e", "type": "github" }, "original": { diff --git a/hosts/lio/flake.nix b/hosts/lio/flake.nix index 77c9ec8..5906885 100644 --- a/hosts/lio/flake.nix +++ b/hosts/lio/flake.nix @@ -42,6 +42,7 @@ modules = [ ./configuration.nix ./hardware-configuration.nix + ./containers_test.nix ( { pkgs, ... }: { diff --git a/onboard.nix b/onboard.nix new file mode 100644 index 0000000..739c0e1 --- /dev/null +++ b/onboard.nix @@ -0,0 +1,48 @@ +{ pkgs, ... }: +{ + networking.hostName = "%%HOSTNAME%%"; + networking.networkmanager.enable = true; + + services.openssh.enable = true; + networking.firewall.allowedTCPPorts = [ 22 ]; + + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + + environment.systemPackages = with pkgs; [ + vim + curl + git + sudo + ]; + + users.users.%%USERNAME%% = { + initialPassword = "password1"; + isNormalUser = true; + extraGroups = [ "wheel" "networkmanager" "video" "input" ]; + }; + + # Ensure SSH key pair generation for non-root users + systemd.services.generate_ssh_key = { + description = "Generate SSH key pair for %%USERNAME%%"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "%%USERNAME%%"; + Type = "oneshot"; + }; + script = '' + #!/run/current-system/sw/bin/bash + if [ ! -f /home/%%USERNAME%%/.ssh/id_ed25519 ]; then + if [ -v DRY_RUN ]; then + echo "DRY_RUN is set. Would generate SSH key for %%USERNAME%%." + else + echo "Generating SSH key for %%USERNAME%%." + mkdir -p /home/%%USERNAME%%/.ssh + chmod 700 /home/%%USERNAME%%/.ssh + /run/current-system/sw/bin/ssh-keygen -t ed25519 -f /home/%%USERNAME%%/.ssh/id_ed25519 -N "" + fi + else + echo "SSH key already exists for %%USERNAME%%." + fi + ''; + }; +} diff --git a/onboard.sh b/onboard.sh new file mode 100644 index 0000000..9c5ae26 --- /dev/null +++ b/onboard.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# curl --proto '=https' --tlsv1.2 -sSf https://share.joshuabell.link/nix/onboard.sh + +# Go to nix configuration +cd /mnt/etc/nixos + +# Ask for required variables +VAR_HOST=$HOSTNAME +VAR_USER=$USERNAME +echo "Hostname will be: $VAR_HOST" +echo "Username will be: $VAR_USER" +while true; do + read -p "Do you wish to continue? (y/n)" yn + case $yn in + [Yy]* ) break;; + [Nn]* ) exit;; + * ) echo "Please answer y/n.";; + esac +done + +# Switch to use labels in hardware-configuration +ex +'/fileSystems."\/"' +"/by-uuid" +'s#by-uuid/.*"#by-label/NIXROOT"' \ + +'/fileSystems."\/boot"' +"/by-uuid" +'s#by-uuid/.*"#by-label/NIXBOOT"' \ + +"wq" hardware-configuration.nix +echo "Switched hardware configuration to use labels" +grep "by-uuid" hardware-configuration.nix # Should show nothing, this will help prompt for changes +grep "by-label" hardware-configuration.nix +echo + +echo "TODO add swap section here that asks for sizes..." +echo + +# Download settings needed for initial boot +curl -O https://share.joshuabell.link/nix/onboard.nix +# update username and hostname in onboard file +ex +"%s/%%HOSTNAME%%/$VAR_HOST/g" +"%s/%%USERNAME%%/$VAR_USER/g" +"wq" onboard.nix +# Import onboard file in configuration.nix +ex +"%s#hardware-configuration.nix#hardware-configuration.nix ./onboard.nix#g" +"wq" configuration.nix +echo "Setup onboard.nix in configuration.nix" +echo + +echo "Run \`nixos-install\` to finish then reboot" +echo "It's recommended to verify contents of hardware config first." +echo