|
29 | 29 | # NixOS won't change an existing user's UID live, so this must stay 991. |
30 | 30 | coderUid = 991; |
31 | 31 |
|
| 32 | + # Port the Coder server listens on. Single source of truth: it sets |
| 33 | + # CODER_HTTP_ADDRESS below and every in-box URL that targets the server |
| 34 | + # (coder-redirect, bootstrap, reset, template sync, reaper, logstream). |
| 35 | + coderPort = 3000; |
| 36 | + |
32 | 37 | # .terraformrc pointing terraform at the locally-packaged coderd provider. |
33 | 38 | # No network access needed during `terraform init`. |
34 | 39 | terraformrc = pkgs.writeText "terraformrc-coderd" '' |
|
543 | 548 | wants = [ "user@${toString coderUid}.service" ]; # non-fatal if user session is delayed |
544 | 549 |
|
545 | 550 | environment = { |
546 | | - CODER_HTTP_ADDRESS = "0.0.0.0:3000"; |
| 551 | + CODER_HTTP_ADDRESS = "0.0.0.0:${toString coderPort}"; |
547 | 552 | CODER_MAX_TOKEN_LIFETIME = "8760h"; # allow year-long tokens (e.g. nixos-sync) |
548 | 553 | CODER_MAX_ADMIN_TOKEN_LIFETIME = "8760h"; |
549 | 554 | # CODER_ACCESS_URL not set → Coder auto-creates a *.try.coder.app tunnel URL |
|
555 | 560 | let |
556 | 561 | inherit (config.services.coder-nixos) lanIp; |
557 | 562 | in |
558 | | - if lanIp != "" then "http://${lanIp}:3000" else "http://${config.networking.hostName}.local:3000"; |
| 563 | + if lanIp != "" then |
| 564 | + "http://${lanIp}:${toString coderPort}" |
| 565 | + else |
| 566 | + "http://${config.networking.hostName}.local:${toString coderPort}"; |
559 | 567 | CODER_PG_CONNECTION_URL = "postgres:///coder?host=/run/postgresql&user=coder&sslmode=disable"; |
560 | 568 | CODER_DATA_DIR = "/var/lib/coder"; |
561 | 569 | # Point the Terraform Docker provider at the rootless Podman socket. |
|
614 | 622 |
|
615 | 623 | if [ -z "''${INITIAL_USER_EMAIL:-}" ]; then |
616 | 624 | echo "INITIAL_USER_EMAIL not set, skipping bootstrap." |
617 | | - echo "Complete the first-run wizard at http://$(${pkgs.nettools}/bin/hostname -s).local:3000" |
| 625 | + echo "Complete the first-run wizard at http://$(${pkgs.nettools}/bin/hostname -s).local:${toString coderPort}" |
618 | 626 | exit 0 |
619 | 627 | fi |
620 | 628 |
|
|
624 | 632 | # means the DB schema and coder role exist. |
625 | 633 | echo "Waiting for coder API..." |
626 | 634 | for i in $(seq 1 60); do |
627 | | - if ${pkgs.curl}/bin/curl -sf http://localhost:3000/api/v2/buildinfo > /dev/null 2>&1; then |
| 635 | + if ${pkgs.curl}/bin/curl -sf http://localhost:${toString coderPort}/api/v2/buildinfo > /dev/null 2>&1; then |
628 | 636 | echo "coder API ready after $((i * 2))s." |
629 | 637 | break |
630 | 638 | fi |
|
654 | 662 | echo "Session token already exists." |
655 | 663 | else |
656 | 664 | echo "Logging in as admin to mint a long-lived token..." |
657 | | - SESSION=$(${pkgs.curl}/bin/curl -sf -X POST http://localhost:3000/api/v2/users/login \ |
| 665 | + SESSION=$(${pkgs.curl}/bin/curl -sf -X POST http://localhost:${toString coderPort}/api/v2/users/login \ |
658 | 666 | -H 'Content-Type: application/json' \ |
659 | 667 | -d "{\"email\":\"$INITIAL_USER_EMAIL\",\"password\":\"$INITIAL_USER_PASSWORD\"}" \ |
660 | 668 | | ${pkgs.jq}/bin/jq -r '.session_token') |
661 | 669 | [ -n "$SESSION" ] && [ "$SESSION" != "null" ] \ |
662 | 670 | || { echo "Admin login failed." >&2; exit 1; } |
663 | | - LONG_TOKEN=$(CODER_URL=http://localhost:3000 CODER_SESSION_TOKEN="$SESSION" \ |
| 671 | + LONG_TOKEN=$(CODER_URL=http://localhost:${toString coderPort} CODER_SESSION_TOKEN="$SESSION" \ |
664 | 672 | ${coder}/bin/coder tokens create --name nixos-sync --lifetime 8760h) |
665 | 673 | [ -n "$LONG_TOKEN" ] \ |
666 | 674 | || { echo "Token mint failed." >&2; exit 1; } |
|
702 | 710 | ${pkgs.terraform}/bin/terraform -chdir="$CODERD_DIR" init -no-color 2>&1 \ |
703 | 711 | | ${pkgs.gnused}/bin/sed 's/^/[template-deploy] /' |
704 | 712 | ${pkgs.terraform}/bin/terraform -chdir="$CODERD_DIR" apply -auto-approve -no-color \ |
705 | | - -var="coder_url=http://localhost:3000" \ |
| 713 | + -var="coder_url=http://localhost:${toString coderPort}" \ |
706 | 714 | -var="coder_session_token=$(cat "$token_file")" \ |
707 | 715 | -var="hostname=${config.networking.hostName}" \ |
708 | 716 | -var="version_name=$COMMIT" \ |
|
775 | 783 | echo "--- starting coder.service" |
776 | 784 | ${pkgs.systemd}/bin/systemctl start coder.service |
777 | 785 | echo "--- waiting for Coder API..." |
778 | | - until ${pkgs.curl}/bin/curl -sf http://localhost:3000/api/v2/buildinfo > /dev/null 2>&1; do |
| 786 | + until ${pkgs.curl}/bin/curl -sf http://localhost:${toString coderPort}/api/v2/buildinfo > /dev/null 2>&1; do |
779 | 787 | sleep 3 |
780 | 788 | done |
781 | 789 |
|
|
786 | 794 | # 8. Mint a fresh long-lived session token using the initial user's creds |
787 | 795 | echo "--- minting session token" |
788 | 796 | SESSION=$(${pkgs.curl}/bin/curl -sf \ |
789 | | - -X POST http://localhost:3000/api/v2/users/login \ |
| 797 | + -X POST http://localhost:${toString coderPort}/api/v2/users/login \ |
790 | 798 | -H 'Content-Type: application/json' \ |
791 | 799 | -d "{\"email\":\"''${INITIAL_USER_EMAIL}\",\"password\":\"''${INITIAL_USER_PASSWORD}\"}" \ |
792 | 800 | | ${pkgs.jq}/bin/jq -r '.session_token') |
793 | | - LONG_TOKEN=$(CODER_URL=http://localhost:3000 CODER_SESSION_TOKEN="$SESSION" \ |
| 801 | + LONG_TOKEN=$(CODER_URL=http://localhost:${toString coderPort} CODER_SESSION_TOKEN="$SESSION" \ |
794 | 802 | ${coder}/bin/coder tokens create --name nixos-sync --lifetime 8760h) |
795 | 803 | echo "$LONG_TOKEN" | ${pkgs.coreutils}/bin/tee /etc/coder/session-token > /dev/null |
796 | 804 | echo "--- session token written" |
|
839 | 847 | ${pkgs.terraform}/bin/terraform -chdir="$CODERD_DIR" init -no-color 2>&1 \ |
840 | 848 | | ${pkgs.gnused}/bin/sed 's/^/[template-sync] /' || true |
841 | 849 | ${pkgs.terraform}/bin/terraform -chdir="$CODERD_DIR" apply -auto-approve -no-color \ |
842 | | - -var="coder_url=http://localhost:3000" \ |
| 850 | + -var="coder_url=http://localhost:${toString coderPort}" \ |
843 | 851 | -var="coder_session_token=$(cat "$TOKEN_FILE")" \ |
844 | 852 | -var="hostname=${config.networking.hostName}" \ |
845 | 853 | -var="version_name=$COMMIT" \ |
|
885 | 893 | Restart = "on-failure"; |
886 | 894 | RestartSec = "10s"; |
887 | 895 | ExecStart = pkgs.writeShellScript "coder-redirect" '' |
888 | | - set -euo pipefail |
889 | | - CODER_LOCAL="http://localhost:3000" |
890 | | -
|
891 | | - # Wait until the Coder API is up |
892 | | - echo "coder-redirect: waiting for Coder API..." |
893 | | - until ${pkgs.curl}/bin/curl -sf "$CODER_LOCAL/api/v2/buildinfo" > /dev/null 2>&1; do |
894 | | - sleep 5 |
895 | | - done |
896 | | -
|
897 | | - # Fetch the tunnel URL (may take a moment to establish after startup) |
898 | | - TUNNEL_URL="" |
899 | | - for i in $(seq 1 20); do |
900 | | - TUNNEL_URL=$(${pkgs.curl}/bin/curl -sf \ |
901 | | - -H "Coder-Session-Token: $(cat /etc/coder/session-token)" \ |
902 | | - "$CODER_LOCAL/api/v2/deployment/config" \ |
903 | | - | ${pkgs.jq}/bin/jq -r '.config.access_url // empty' 2>/dev/null || true) |
904 | | - if echo "$TUNNEL_URL" | grep -q "try.coder.app"; then |
905 | | - echo "coder-redirect: tunnel URL is $TUNNEL_URL" |
906 | | - break |
907 | | - fi |
908 | | - echo "coder-redirect: tunnel not ready yet (attempt $i), retrying in 5s..." |
909 | | - sleep 5 |
910 | | - done |
911 | | -
|
912 | | - if ! echo "$TUNNEL_URL" | grep -q "try.coder.app"; then |
913 | | - echo "coder-redirect: could not detect tunnel URL; will retry in 30s" |
914 | | - sleep 30 |
915 | | - exit 1 |
916 | | - fi |
917 | | -
|
918 | | - export CODER_TUNNEL_URL="$TUNNEL_URL" |
919 | | -
|
920 | | - # Surface the tunnel URL on every console / SSH login. |
921 | | - HOSTNAME="$(${pkgs.nettools}/bin/hostname)" |
922 | | - ${pkgs.coreutils}/bin/cat > /etc/motd <<EOF |
923 | | -
|
924 | | - Coder is running on this box. |
925 | | -
|
926 | | - Tunnel URL: $TUNNEL_URL |
927 | | - Local: http://$HOSTNAME.local:3000 |
928 | | - Redirect: http://$HOSTNAME.local (302 → tunnel) |
929 | | -
|
930 | | - EOF |
931 | | -
|
932 | | - exec ${pkgs.python3}/bin/python3 ${redirectPy} |
| 896 | + set -euo pipefail |
| 897 | + CODER_LOCAL="http://localhost:${toString coderPort}" |
| 898 | +
|
| 899 | + # write_accessUrl <text>: publish <text> to the file the login banner |
| 900 | + # reads (see environment.interactiveShellInit below). Best-effort so |
| 901 | + # a /tmp write can't abort the service under set -e. |
| 902 | + write_accessUrl() { |
| 903 | + ${pkgs.coreutils}/bin/printf '%s\n' "$1" > /tmp/coder-access-url \ |
| 904 | + && ${pkgs.coreutils}/bin/chmod 0644 /tmp/coder-access-url || true |
| 905 | + } |
| 906 | +
|
| 907 | + # Seed with the local URL so a terminal opened before the tunnel is |
| 908 | + # up still shows a reachable URL; upgraded to "<tunnel> (<local>)" |
| 909 | + # below. This also overwrites any stale value from a previous run. |
| 910 | + write_accessUrl "$CODER_LOCAL" |
| 911 | +
|
| 912 | + # Wait until the Coder API is up |
| 913 | + echo "coder-redirect: waiting for Coder API..." |
| 914 | + until ${pkgs.curl}/bin/curl -sf "$CODER_LOCAL/api/v2/buildinfo" > /dev/null 2>&1; do |
| 915 | + sleep 5 |
| 916 | + done |
| 917 | +
|
| 918 | + # Fetch the tunnel URL (may take a moment to establish after startup) |
| 919 | + TUNNEL_URL="" |
| 920 | + for i in $(seq 1 20); do |
| 921 | + TUNNEL_URL=$(${pkgs.curl}/bin/curl -sf \ |
| 922 | + -H "Coder-Session-Token: $(cat /etc/coder/session-token)" \ |
| 923 | + "$CODER_LOCAL/api/v2/deployment/config" \ |
| 924 | + | ${pkgs.jq}/bin/jq -r '.config.access_url // empty' 2>/dev/null || true) |
| 925 | + if echo "$TUNNEL_URL" | grep -q "try.coder.app"; then |
| 926 | + echo "coder-redirect: tunnel URL is $TUNNEL_URL" |
| 927 | + break |
| 928 | + fi |
| 929 | + echo "coder-redirect: tunnel not ready yet (attempt $i), retrying in 5s..." |
| 930 | + sleep 5 |
| 931 | + done |
| 932 | +
|
| 933 | + if ! echo "$TUNNEL_URL" | grep -q "try.coder.app"; then |
| 934 | + echo "coder-redirect: could not detect tunnel URL; will retry in 30s" |
| 935 | + sleep 30 |
| 936 | + exit 1 |
| 937 | + fi |
| 938 | +
|
| 939 | + export CODER_TUNNEL_URL="$TUNNEL_URL" |
| 940 | +
|
| 941 | + # Upgrade to "<access URL> (<local URL>)" now that the tunnel URL is |
| 942 | + # known, so terminals show both on login. |
| 943 | + write_accessUrl "$TUNNEL_URL ($CODER_LOCAL)" |
| 944 | +
|
| 945 | + exec ${pkgs.python3}/bin/python3 ${redirectPy} |
933 | 946 | ''; |
934 | 947 | }; |
935 | 948 | }; |
936 | 949 |
|
| 950 | + # ── Coder access URL login banner ───────────────────────────────────────── |
| 951 | + # coder-redirect seeds /tmp/coder-access-url with the local URL and upgrades |
| 952 | + # it to "<access URL> (<local URL>)" once the tunnel is up. Print it on |
| 953 | + # interactive shells so both a local terminal and an SSH session show where |
| 954 | + # to reach Coder — the old /etc/motd only surfaced on PAM logins, never in a |
| 955 | + # desktop terminal. This only reads the file; it never touches the network. |
| 956 | + # The exported guard keeps nested shells from reprinting it within a session. |
| 957 | + environment.interactiveShellInit = '' |
| 958 | + if [ -z "''${CODER_ACCESS_URL_SHOWN:-}" ] && [ -s /tmp/coder-access-url ]; then |
| 959 | + export CODER_ACCESS_URL_SHOWN=1 |
| 960 | + printf '\n Coder is running on this box: %s\n\n' "$(cat /tmp/coder-access-url)" |
| 961 | + fi |
| 962 | + ''; |
| 963 | + |
937 | 964 | # ── Workspace reaper ────────────────────────────────────────────────────────── |
938 | 965 | # Deletes workspaces that have been stopped for >= 72 h. |
939 | 966 | # time_til_dormant_autodelete_ms is Enterprise-only so we implement this |
|
947 | 974 | User = "root"; |
948 | 975 | ExecStart = pkgs.writeShellScript "coder-workspace-reaper" '' |
949 | 976 | set -euo pipefail |
950 | | - CODER_LOCAL="http://localhost:3000" |
| 977 | + CODER_LOCAL="http://localhost:${toString coderPort}" |
951 | 978 | TOKEN_FILE="/etc/coder/session-token" |
952 | 979 | DELETE_AFTER_HOURS=72 |
953 | 980 |
|
|
1035 | 1062 | coder-logstream-kube/coder-logstream-kube \ |
1036 | 1063 | --namespace coder-workspaces \ |
1037 | 1064 | --create-namespace \ |
1038 | | - --set url=http://10.42.0.1:3000 \ |
| 1065 | + --set url=http://10.42.0.1:${toString coderPort} \ |
1039 | 1066 | --set namespaces={coder-workspaces} \ |
1040 | 1067 | --atomic --timeout 120s |
1041 | 1068 |
|
|
0 commit comments