diff --git a/.github/workflows/versions.yml b/.github/workflows/versions.yml
index 9a138ebb..4b539a68 100644
--- a/.github/workflows/versions.yml
+++ b/.github/workflows/versions.yml
@@ -51,6 +51,26 @@ jobs:
           __tests__/verify-node.sh "${BASH_REMATCH[1]}"
         shell: bash
 
+  nightly-syntax:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest, windows-latest, macos-latest]
+        node-version: [17-nightly, 18-nightly, 19-nightly]
+    steps:
+      - uses: actions/checkout@v3
+      - name: Setup Node
+        uses: ./
+        with:
+          node-version: ${{ matrix.node-version }}
+      - name: Verify node and npm
+        run: |
+          nightlyVersion="${{ matrix.node-version }}"
+          majorVersion=$(echo $nightlyVersion | cut -d- -f1)
+          __tests__/verify-node.sh "$majorVersion"
+        shell: bash
+
   manifest:
     runs-on: ${{ matrix.os }}
     strategy:
diff --git a/__tests__/authutil.test.ts b/__tests__/authutil.test.ts
index 1ec4e1e1..594c6a13 100644
--- a/__tests__/authutil.test.ts
+++ b/__tests__/authutil.test.ts
@@ -1,4 +1,4 @@
-import os = require('os');
+import os from 'os';
 import * as fs from 'fs';
 import * as path from 'path';
 import * as core from '@actions/core';
diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts
index 28d77e7c..d359f7ab 100644
--- a/__tests__/installer.test.ts
+++ b/__tests__/installer.test.ts
@@ -6,7 +6,7 @@ import * as im from '../src/installer';
 import * as cache from '@actions/cache';
 import fs from 'fs';
 import cp from 'child_process';
-import osm = require('os');
+import osm from 'os';
 import path from 'path';
 import each from 'jest-each';
 import * as main from '../src/main';
@@ -138,7 +138,8 @@ describe('setup-node', () => {
   });
 
   it('can mock dist versions', async () => {
-    let versions: im.INodeVersion[] = await im.getVersionsFromDist();
+    const versionSpec = '1.2.3';
+    let versions: im.INodeVersion[] = await im.getVersionsFromDist(versionSpec);
     expect(versions).toBeDefined();
     expect(versions?.length).toBe(23);
   });
diff --git a/dist/setup/index.js b/dist/setup/index.js
index 0daaa2f2..0be3cc24 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -73194,6 +73194,7 @@ function getNode(versionSpec, stable, checkLatest, auth, arch = os.arch()) {
         // Store manifest data to avoid multiple calls
         let manifest;
         let nodeVersions;
+        let isNightly = versionSpec.includes('nightly');
         let osPlat = os.platform();
         let osArch = translateArchToDistUrl(arch);
         if (isLtsAlias(versionSpec)) {
@@ -73203,11 +73204,15 @@ function getNode(versionSpec, stable, checkLatest, auth, arch = os.arch()) {
             versionSpec = resolveLtsAliasFromManifest(versionSpec, stable, manifest);
         }
         if (isLatestSyntax(versionSpec)) {
-            nodeVersions = yield getVersionsFromDist();
+            nodeVersions = yield getVersionsFromDist(versionSpec);
             versionSpec = yield queryDistForMatch(versionSpec, arch, nodeVersions);
             core.info(`getting latest node version...`);
         }
-        if (checkLatest) {
+        if (isNightly && checkLatest) {
+            nodeVersions = yield getVersionsFromDist(versionSpec);
+            versionSpec = yield queryDistForMatch(versionSpec, arch, nodeVersions);
+        }
+        if (checkLatest && !isNightly) {
             core.info('Attempt to resolve the latest version from manifest...');
             const resolvedVersion = yield resolveVersionFromManifest(versionSpec, stable, auth, osArch, manifest);
             if (resolvedVersion) {
@@ -73220,7 +73225,13 @@ function getNode(versionSpec, stable, checkLatest, auth, arch = os.arch()) {
         }
         // check cache
         let toolPath;
-        toolPath = tc.find('node', versionSpec, osArch);
+        if (isNightly) {
+            const nightlyVersion = findNightlyVersionInHostedToolcache(versionSpec, osArch);
+            toolPath = nightlyVersion && tc.find('node', nightlyVersion, osArch);
+        }
+        else {
+            toolPath = tc.find('node', versionSpec, osArch);
+        }
         // If not found in cache, download
         if (toolPath) {
             core.info(`Found in cache @ ${toolPath}`);
@@ -73316,6 +73327,11 @@ function getNode(versionSpec, stable, checkLatest, auth, arch = os.arch()) {
     });
 }
 exports.getNode = getNode;
+function findNightlyVersionInHostedToolcache(versionsSpec, osArch) {
+    const foundAllVersions = tc.findAllVersions('node', osArch);
+    const version = evaluateVersions(foundAllVersions, versionsSpec);
+    return version;
+}
 function isLtsAlias(versionSpec) {
     return versionSpec.startsWith('lts/');
 }
@@ -73382,7 +73398,8 @@ function getInfoFromDist(versionSpec, arch = os.arch(), nodeVersions) {
             ? `node-v${version}-win-${osArch}`
             : `node-v${version}-${osPlat}-${osArch}`;
         let urlFileName = osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`;
-        let url = `https://nodejs.org/dist/v${version}/${urlFileName}`;
+        const initialUrl = getNodejsDistUrl(versionSpec);
+        const url = `${initialUrl}/v${version}/${urlFileName}`;
         return {
             downloadUrl: url,
             resolvedVersion: version,
@@ -73403,10 +73420,51 @@ function resolveVersionFromManifest(versionSpec, stable, auth, osArch = translat
         }
     });
 }
+function evaluateNightlyVersions(versions, versionSpec) {
+    let version = '';
+    let range;
+    const [raw, prerelease] = versionSpec.split('-');
+    const isValidVersion = semver.valid(raw);
+    const rawVersion = isValidVersion ? raw : semver.coerce(raw);
+    if (rawVersion) {
+        if (prerelease !== 'nightly') {
+            range = `${rawVersion}+${prerelease.replace('nightly', 'nightly.')}`;
+        }
+        else {
+            range = semver.validRange(`^${rawVersion}`);
+        }
+    }
+    if (range) {
+        versions = versions.sort((a, b) => {
+            if (semver.gt(a, b)) {
+                return 1;
+            }
+            return -1;
+        });
+        for (let i = versions.length - 1; i >= 0; i--) {
+            const potential = versions[i];
+            const satisfied = semver.satisfies(potential.replace('-nightly', '+nightly.'), range);
+            if (satisfied) {
+                version = potential;
+                break;
+            }
+        }
+    }
+    if (version) {
+        core.debug(`matched: ${version}`);
+    }
+    else {
+        core.debug('match not found');
+    }
+    return version;
+}
 // TODO - should we just export this from @actions/tool-cache? Lifted directly from there
 function evaluateVersions(versions, versionSpec) {
     let version = '';
     core.debug(`evaluating ${versions.length} versions`);
+    if (versionSpec.includes('nightly')) {
+        return evaluateNightlyVersions(versions, versionSpec);
+    }
     versions = versions.sort((a, b) => {
         if (semver.gt(a, b)) {
             return 1;
@@ -73429,6 +73487,18 @@ function evaluateVersions(versions, versionSpec) {
     }
     return version;
 }
+function getNodejsDistUrl(version) {
+    const prerelease = semver.prerelease(version);
+    if (version.includes('nightly')) {
+        return 'https://nodejs.org/download/nightly';
+    }
+    else if (!prerelease) {
+        return 'https://nodejs.org/dist';
+    }
+    else {
+        return 'https://nodejs.org/download/rc';
+    }
+}
 function queryDistForMatch(versionSpec, arch = os.arch(), nodeVersions) {
     return __awaiter(this, void 0, void 0, function* () {
         let osPlat = os.platform();
@@ -73450,7 +73520,7 @@ function queryDistForMatch(versionSpec, arch = os.arch(), nodeVersions) {
         }
         if (!nodeVersions) {
             core.debug('No dist manifest cached');
-            nodeVersions = yield getVersionsFromDist();
+            nodeVersions = yield getVersionsFromDist(versionSpec);
         }
         let versions = [];
         if (isLatestSyntax(versionSpec)) {
@@ -73468,9 +73538,10 @@ function queryDistForMatch(versionSpec, arch = os.arch(), nodeVersions) {
         return version;
     });
 }
-function getVersionsFromDist() {
+function getVersionsFromDist(versionSpec) {
     return __awaiter(this, void 0, void 0, function* () {
-        let dataUrl = 'https://nodejs.org/dist/index.json';
+        const initialUrl = getNodejsDistUrl(versionSpec);
+        const dataUrl = `${initialUrl}/index.json`;
         let httpClient = new hc.HttpClient('setup-node', [], {
             allowRetries: true,
             maxRetries: 3
@@ -73494,6 +73565,7 @@ exports.getVersionsFromDist = getVersionsFromDist;
 // and lib file in a folder, not zipped.
 function acquireNodeFromFallbackLocation(version, arch = os.arch()) {
     return __awaiter(this, void 0, void 0, function* () {
+        const initialUrl = getNodejsDistUrl(version);
         let osPlat = os.platform();
         let osArch = translateArchToDistUrl(arch);
         // Create temporary folder to download in to
@@ -73505,8 +73577,8 @@ function acquireNodeFromFallbackLocation(version, arch = os.arch()) {
         let exeUrl;
         let libUrl;
         try {
-            exeUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.exe`;
-            libUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.lib`;
+            exeUrl = `${initialUrl}/v${version}/win-${osArch}/node.exe`;
+            libUrl = `${initialUrl}/v${version}/win-${osArch}/node.lib`;
             core.info(`Downloading only node binary from ${exeUrl}`);
             const exePath = yield tc.downloadTool(exeUrl);
             yield io.cp(exePath, path.join(tempDir, 'node.exe'));
@@ -73515,8 +73587,8 @@ function acquireNodeFromFallbackLocation(version, arch = os.arch()) {
         }
         catch (err) {
             if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
-                exeUrl = `https://nodejs.org/dist/v${version}/node.exe`;
-                libUrl = `https://nodejs.org/dist/v${version}/node.lib`;
+                exeUrl = `${initialUrl}/v${version}/node.exe`;
+                libUrl = `${initialUrl}/v${version}/node.lib`;
                 const exePath = yield tc.downloadTool(exeUrl);
                 yield io.cp(exePath, path.join(tempDir, 'node.exe'));
                 const libPath = yield tc.downloadTool(libUrl);
@@ -73607,7 +73679,7 @@ const auth = __importStar(__nccwpck_require__(7573));
 const path = __importStar(__nccwpck_require__(1017));
 const cache_restore_1 = __nccwpck_require__(9517);
 const cache_utils_1 = __nccwpck_require__(1678);
-const os = __nccwpck_require__(2037);
+const os_1 = __importDefault(__nccwpck_require__(2037));
 function run() {
     return __awaiter(this, void 0, void 0, function* () {
         try {
@@ -73615,7 +73687,7 @@ function run() {
             // Version is optional.  If supplied, install / use from the tool cache
             // If not supplied then task is still used to setup proxy, auth, etc...
             //
-            let version = resolveVersionInput();
+            const version = resolveVersionInput();
             let arch = core.getInput('architecture');
             const cache = core.getInput('cache');
             // if architecture supplied but node-version is not
@@ -73624,12 +73696,12 @@ function run() {
                 core.warning('`architecture` is provided but `node-version` is missing. In this configuration, the version/architecture of Node will not be changed. To fix this, provide `architecture` in combination with `node-version`');
             }
             if (!arch) {
-                arch = os.arch();
+                arch = os_1.default.arch();
             }
             if (version) {
-                let token = core.getInput('token');
-                let auth = !token || cache_utils_1.isGhes() ? undefined : `token ${token}`;
-                let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
+                const token = core.getInput('token');
+                const auth = !token || cache_utils_1.isGhes() ? undefined : `token ${token}`;
+                const stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
                 const checkLatest = (core.getInput('check-latest') || 'false').toUpperCase() === 'TRUE';
                 yield installer.getNode(version, stable, checkLatest, auth, arch);
             }
diff --git a/src/installer.ts b/src/installer.ts
index 83a43d85..53ace18e 100644
--- a/src/installer.ts
+++ b/src/installer.ts
@@ -11,6 +11,8 @@ import fs = require('fs');
 //
 // Node versions interface
 // see https://nodejs.org/dist/index.json
+// for nightly https://nodejs.org/download/nightly/index.json
+// for rc https://nodejs.org/download/rc/index.json
 //
 export interface INodeVersion {
   version: string;
@@ -38,6 +40,7 @@ export async function getNode(
   // Store manifest data to avoid multiple calls
   let manifest: INodeRelease[] | undefined;
   let nodeVersions: INodeVersion[] | undefined;
+  let isNightly = versionSpec.includes('nightly');
   let osPlat: string = os.platform();
   let osArch: string = translateArchToDistUrl(arch);
 
@@ -51,12 +54,17 @@ export async function getNode(
   }
 
   if (isLatestSyntax(versionSpec)) {
-    nodeVersions = await getVersionsFromDist();
+    nodeVersions = await getVersionsFromDist(versionSpec);
     versionSpec = await queryDistForMatch(versionSpec, arch, nodeVersions);
     core.info(`getting latest node version...`);
   }
 
-  if (checkLatest) {
+  if (isNightly && checkLatest) {
+    nodeVersions = await getVersionsFromDist(versionSpec);
+    versionSpec = await queryDistForMatch(versionSpec, arch, nodeVersions);
+  }
+
+  if (checkLatest && !isNightly) {
     core.info('Attempt to resolve the latest version from manifest...');
     const resolvedVersion = await resolveVersionFromManifest(
       versionSpec,
@@ -75,7 +83,15 @@ export async function getNode(
 
   // check cache
   let toolPath: string;
-  toolPath = tc.find('node', versionSpec, osArch);
+  if (isNightly) {
+    const nightlyVersion = findNightlyVersionInHostedToolcache(
+      versionSpec,
+      osArch
+    );
+    toolPath = nightlyVersion && tc.find('node', nightlyVersion, osArch);
+  } else {
+    toolPath = tc.find('node', versionSpec, osArch);
+  }
 
   // If not found in cache, download
   if (toolPath) {
@@ -199,6 +215,16 @@ export async function getNode(
   core.addPath(toolPath);
 }
 
+function findNightlyVersionInHostedToolcache(
+  versionsSpec: string,
+  osArch: string
+) {
+  const foundAllVersions = tc.findAllVersions('node', osArch);
+  const version = evaluateVersions(foundAllVersions, versionsSpec);
+
+  return version;
+}
+
 function isLtsAlias(versionSpec: string): boolean {
   return versionSpec.startsWith('lts/');
 }
@@ -306,7 +332,8 @@ async function getInfoFromDist(
       : `node-v${version}-${osPlat}-${osArch}`;
   let urlFileName: string =
     osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`;
-  let url = `https://nodejs.org/dist/v${version}/${urlFileName}`;
+  const initialUrl = getNodejsDistUrl(versionSpec);
+  const url = `${initialUrl}/v${version}/${urlFileName}`;
 
   return <INodeVersionInfo>{
     downloadUrl: url,
@@ -338,10 +365,61 @@ async function resolveVersionFromManifest(
   }
 }
 
+function evaluateNightlyVersions(
+  versions: string[],
+  versionSpec: string
+): string {
+  let version = '';
+  let range: string | null | undefined;
+  const [raw, prerelease] = versionSpec.split('-');
+  const isValidVersion = semver.valid(raw);
+  const rawVersion = isValidVersion ? raw : semver.coerce(raw);
+  if (rawVersion) {
+    if (prerelease !== 'nightly') {
+      range = `${rawVersion}+${prerelease.replace('nightly', 'nightly.')}`;
+    } else {
+      range = semver.validRange(`^${rawVersion}`);
+    }
+  }
+
+  if (range) {
+    versions = versions.sort((a, b) => {
+      if (semver.gt(a, b)) {
+        return 1;
+      }
+      return -1;
+    });
+    for (let i = versions.length - 1; i >= 0; i--) {
+      const potential: string = versions[i];
+      const satisfied: boolean = semver.satisfies(
+        potential.replace('-nightly', '+nightly.'),
+        range
+      );
+      if (satisfied) {
+        version = potential;
+        break;
+      }
+    }
+  }
+
+  if (version) {
+    core.debug(`matched: ${version}`);
+  } else {
+    core.debug('match not found');
+  }
+
+  return version;
+}
+
 // TODO - should we just export this from @actions/tool-cache? Lifted directly from there
 function evaluateVersions(versions: string[], versionSpec: string): string {
   let version = '';
   core.debug(`evaluating ${versions.length} versions`);
+
+  if (versionSpec.includes('nightly')) {
+    return evaluateNightlyVersions(versions, versionSpec);
+  }
+
   versions = versions.sort((a, b) => {
     if (semver.gt(a, b)) {
       return 1;
@@ -366,6 +444,17 @@ function evaluateVersions(versions: string[], versionSpec: string): string {
   return version;
 }
 
+function getNodejsDistUrl(version: string) {
+  const prerelease = semver.prerelease(version);
+  if (version.includes('nightly')) {
+    return 'https://nodejs.org/download/nightly';
+  } else if (!prerelease) {
+    return 'https://nodejs.org/dist';
+  } else {
+    return 'https://nodejs.org/download/rc';
+  }
+}
+
 async function queryDistForMatch(
   versionSpec: string,
   arch: string = os.arch(),
@@ -392,7 +481,7 @@ async function queryDistForMatch(
 
   if (!nodeVersions) {
     core.debug('No dist manifest cached');
-    nodeVersions = await getVersionsFromDist();
+    nodeVersions = await getVersionsFromDist(versionSpec);
   }
 
   let versions: string[] = [];
@@ -410,12 +499,15 @@ async function queryDistForMatch(
   });
 
   // get the latest version that matches the version spec
-  let version: string = evaluateVersions(versions, versionSpec);
+  let version = evaluateVersions(versions, versionSpec);
   return version;
 }
 
-export async function getVersionsFromDist(): Promise<INodeVersion[]> {
-  let dataUrl = 'https://nodejs.org/dist/index.json';
+export async function getVersionsFromDist(
+  versionSpec: string
+): Promise<INodeVersion[]> {
+  const initialUrl = getNodejsDistUrl(versionSpec);
+  const dataUrl = `${initialUrl}/index.json`;
   let httpClient = new hc.HttpClient('setup-node', [], {
     allowRetries: true,
     maxRetries: 3
@@ -440,6 +532,7 @@ async function acquireNodeFromFallbackLocation(
   version: string,
   arch: string = os.arch()
 ): Promise<string> {
+  const initialUrl = getNodejsDistUrl(version);
   let osPlat: string = os.platform();
   let osArch: string = translateArchToDistUrl(arch);
 
@@ -453,8 +546,8 @@ async function acquireNodeFromFallbackLocation(
   let exeUrl: string;
   let libUrl: string;
   try {
-    exeUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.exe`;
-    libUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.lib`;
+    exeUrl = `${initialUrl}/v${version}/win-${osArch}/node.exe`;
+    libUrl = `${initialUrl}/v${version}/win-${osArch}/node.lib`;
 
     core.info(`Downloading only node binary from ${exeUrl}`);
 
@@ -464,8 +557,8 @@ async function acquireNodeFromFallbackLocation(
     await io.cp(libPath, path.join(tempDir, 'node.lib'));
   } catch (err) {
     if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
-      exeUrl = `https://nodejs.org/dist/v${version}/node.exe`;
-      libUrl = `https://nodejs.org/dist/v${version}/node.lib`;
+      exeUrl = `${initialUrl}/v${version}/node.exe`;
+      libUrl = `${initialUrl}/v${version}/node.lib`;
 
       const exePath = await tc.downloadTool(exeUrl);
       await io.cp(exePath, path.join(tempDir, 'node.exe'));
diff --git a/src/main.ts b/src/main.ts
index 6a980a0d..3faf67c7 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -6,7 +6,7 @@ import * as auth from './authutil';
 import * as path from 'path';
 import {restoreCache} from './cache-restore';
 import {isGhes, isCacheFeatureAvailable} from './cache-utils';
-import os = require('os');
+import os from 'os';
 
 export async function run() {
   try {
@@ -14,7 +14,7 @@ export async function run() {
     // Version is optional.  If supplied, install / use from the tool cache
     // If not supplied then task is still used to setup proxy, auth, etc...
     //
-    let version = resolveVersionInput();
+    const version = resolveVersionInput();
 
     let arch = core.getInput('architecture');
     const cache = core.getInput('cache');
@@ -32,9 +32,10 @@ export async function run() {
     }
 
     if (version) {
-      let token = core.getInput('token');
-      let auth = !token || isGhes() ? undefined : `token ${token}`;
-      let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
+      const token = core.getInput('token');
+      const auth = !token || isGhes() ? undefined : `token ${token}`;
+      const stable =
+        (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
       const checkLatest =
         (core.getInput('check-latest') || 'false').toUpperCase() === 'TRUE';
       await installer.getNode(version, stable, checkLatest, auth, arch);