diff --git a/src/commands.ts b/src/commands.ts
index 4db02219..ccc2c653 100644
--- a/src/commands.ts
+++ b/src/commands.ts
@@ -1,5 +1,5 @@
 import axios from "axios"
-import { getUser, getWorkspaces, updateWorkspaceVersion } from "coder/site/src/api/api"
+import { getAuthenticatedUser, getWorkspaces, updateWorkspaceVersion } from "coder/site/src/api/api"
 import { Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated"
 import * as vscode from "vscode"
 import { Remote } from "./remote"
@@ -73,21 +73,28 @@ export class Commands {
 
     await this.storage.setURL(url)
     await this.storage.setSessionToken(token)
-    const user = await getUser()
-    await vscode.commands.executeCommand("setContext", "coder.authenticated", true)
-    vscode.window
-      .showInformationMessage(
-        `Welcome to Coder, ${user.username}!`,
-        {
-          detail: "You can now use the Coder extension to manage your Coder instance.",
-        },
-        "Open Workspace",
-      )
-      .then((action) => {
-        if (action === "Open Workspace") {
-          vscode.commands.executeCommand("coder.open")
-        }
-      })
+    try {
+      const user = await getAuthenticatedUser()
+      if (!user) {
+        throw new Error("Failed to get authenticated user")
+      }
+      await vscode.commands.executeCommand("setContext", "coder.authenticated", true)
+      vscode.window
+        .showInformationMessage(
+          `Welcome to Coder, ${user.username}!`,
+          {
+            detail: "You can now use the Coder extension to manage your Coder instance.",
+          },
+          "Open Workspace",
+        )
+        .then((action) => {
+          if (action === "Open Workspace") {
+            vscode.commands.executeCommand("coder.open")
+          }
+        })
+    } catch (error) {
+      vscode.window.showErrorMessage("Failed to authenticate with Coder: " + error)
+    }
   }
 
   public async logout(): Promise<void> {
diff --git a/src/extension.ts b/src/extension.ts
index a6078ba6..e5e73cd7 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,6 +1,6 @@
 "use strict"
 
-import { getUser } from "coder/site/src/api/api"
+import { getAuthenticatedUser } from "coder/site/src/api/api"
 import * as module from "module"
 import * as vscode from "vscode"
 import { Commands } from "./commands"
@@ -12,7 +12,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
   const storage = new Storage(output, ctx.globalState, ctx.secrets, ctx.globalStorageUri, ctx.logUri)
   await storage.init()
 
-  getUser()
+  getAuthenticatedUser()
     .then(() => {
       vscode.commands.executeCommand("setContext", "coder.authenticated", true)
     })
diff --git a/src/remote.ts b/src/remote.ts
index cf47f965..8c11c1aa 100644
--- a/src/remote.ts
+++ b/src/remote.ts
@@ -6,8 +6,9 @@ import {
   getWorkspaceBuildLogs,
   getWorkspaceByOwnerAndName,
   startWorkspace,
+  getDeploymentSSHConfig,
 } from "coder/site/src/api/api"
-import { ProvisionerJobLog, Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated"
+import { ProvisionerJobLog, SSHConfigResponse, Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated"
 import EventSource from "eventsource"
 import find from "find-process"
 import * as fs from "fs/promises"
@@ -18,7 +19,7 @@ import prettyBytes from "pretty-bytes"
 import * as semver from "semver"
 import * as vscode from "vscode"
 import * as ws from "ws"
-import { SSHConfig } from "./sshConfig"
+import { SSHConfig, defaultSSHConfigResponse } from "./sshConfig"
 import { Storage } from "./storage"
 
 export class Remote {
@@ -440,6 +441,29 @@ export class Remote {
   // updateSSHConfig updates the SSH configuration with a wildcard that handles
   // all Coder entries.
   private async updateSSHConfig() {
+    let deploymentConfig: SSHConfigResponse = defaultSSHConfigResponse
+    try {
+      deploymentConfig = await getDeploymentSSHConfig()
+    } catch (error) {
+      if (!axios.isAxiosError(error)) {
+        throw error
+      }
+      switch (error.response?.status) {
+        case 404: {
+          // Deployment does not support overriding ssh config yet. Likely an
+          // older version, just use the default.
+          deploymentConfig = defaultSSHConfigResponse
+          break
+        }
+        case 401: {
+          await this.vscodeProposed.window.showErrorMessage("Your session expired...")
+          throw error
+        }
+        default:
+          throw error
+      }
+    }
+
     let sshConfigFile = vscode.workspace.getConfiguration().get<string>("remote.SSH.configFile")
     if (!sshConfigFile) {
       sshConfigFile = path.join(os.homedir(), ".ssh", "config")
@@ -480,7 +504,7 @@ export class Remote {
       SetEnv: "CODER_SSH_SESSION_TYPE=vscode",
     }
 
-    await sshConfig.update(sshValues)
+    await sshConfig.update(sshValues, deploymentConfig)
   }
 
   // showNetworkUpdates finds the SSH process ID that is being used by this
diff --git a/src/sshConfig.test.ts b/src/sshConfig.test.ts
index c44c5e46..2c20d520 100644
--- a/src/sshConfig.test.ts
+++ b/src/sshConfig.test.ts
@@ -167,3 +167,46 @@ Host coder-vscode--*
     mode: 384,
   })
 })
+
+it("override values", async () => {
+  mockFileSystem.readFile.mockRejectedValueOnce("No file found")
+  const sshConfig = new SSHConfig(sshFilePath, mockFileSystem)
+  await sshConfig.load()
+  await sshConfig.update(
+    {
+      Host: "coder-vscode--*",
+      ProxyCommand: "some-command-here",
+      ConnectTimeout: "0",
+      StrictHostKeyChecking: "no",
+      UserKnownHostsFile: "/dev/null",
+      LogLevel: "ERROR",
+    },
+    {
+      ssh_config_options: {
+        loglevel: "DEBUG", // This tests case insensitive
+        ConnectTimeout: "500",
+        ExtraKey: "ExtraValue",
+        Foo: "bar",
+        Buzz: "baz",
+        // Remove this key
+        StrictHostKeyChecking: "",
+        ExtraRemove: "",
+      },
+      hostname_prefix: "",
+    },
+  )
+
+  const expectedOutput = `# --- START CODER VSCODE ---
+Host coder-vscode--*
+  ProxyCommand some-command-here
+  ConnectTimeout 500
+  UserKnownHostsFile /dev/null
+  LogLevel DEBUG
+  Buzz baz
+  ExtraKey ExtraValue
+  Foo bar
+# --- END CODER VSCODE ---`
+
+  expect(mockFileSystem.readFile).toBeCalledWith(sshFilePath, expect.anything())
+  expect(mockFileSystem.writeFile).toBeCalledWith(sshFilePath, expectedOutput, expect.anything())
+})
diff --git a/src/sshConfig.ts b/src/sshConfig.ts
index 9fdf2bea..21c5a2e7 100644
--- a/src/sshConfig.ts
+++ b/src/sshConfig.ts
@@ -1,3 +1,4 @@
+import { SSHConfigResponse } from "coder/site/src/api/typesGenerated"
 import { writeFile, readFile } from "fs/promises"
 import { ensureDir } from "fs-extra"
 import path from "path"
@@ -30,6 +31,12 @@ const defaultFileSystem: FileSystem = {
   writeFile,
 }
 
+export const defaultSSHConfigResponse: SSHConfigResponse = {
+  ssh_config_options: {},
+  // The prefix is not used by the vscode-extension
+  hostname_prefix: "coder.",
+}
+
 export class SSHConfig {
   private filePath: string
   private fileSystem: FileSystem
@@ -51,7 +58,7 @@ export class SSHConfig {
     }
   }
 
-  async update(values: SSHValues) {
+  async update(values: SSHValues, overrides: SSHConfigResponse = defaultSSHConfigResponse) {
     // We should remove this in March 2023 because there is not going to have
     // old configs
     this.cleanUpOldConfig()
@@ -59,7 +66,7 @@ export class SSHConfig {
     if (block) {
       this.eraseBlock(block)
     }
-    this.appendBlock(values)
+    this.appendBlock(values, overrides.ssh_config_options)
     await this.save()
   }
 
@@ -102,12 +109,59 @@ export class SSHConfig {
     this.raw = this.getRaw().replace(block.raw, "")
   }
 
-  private appendBlock({ Host, ...otherValues }: SSHValues) {
+  /**
+   *
+   * appendBlock builds the ssh config block. The order of the keys is determinstic based on the input.
+   * Expected values are always in a consistent order followed by any additional overrides in sorted order.
+   *
+   * @param param0 - SSHValues are the expected SSH values for using ssh with coder.
+   * @param overrides - Overrides typically come from the deployment api and are used to override the default values.
+   *                    The overrides are given as key:value pairs where the key is the ssh config file key.
+   *                    If the key matches an expected value, the expected value is overridden. If it does not
+   *                    match an expected value, it is appended to the end of the block.
+   */
+  private appendBlock({ Host, ...otherValues }: SSHValues, overrides: Record<string, string>) {
     const lines = [this.startBlockComment, `Host ${Host}`]
+    // We need to do a case insensitive match for the overrides as ssh config keys are case insensitive.
+    // To get the correct key:value, use:
+    //   key = caseInsensitiveOverrides[key.toLowerCase()]
+    //   value = overrides[key]
+    const caseInsensitiveOverrides: Record<string, string> = {}
+    Object.keys(overrides).forEach((key) => {
+      caseInsensitiveOverrides[key.toLowerCase()] = key
+    })
+
     const keys = Object.keys(otherValues) as Array<keyof typeof otherValues>
     keys.forEach((key) => {
+      const lower = key.toLowerCase()
+      if (caseInsensitiveOverrides[lower]) {
+        const correctCaseKey = caseInsensitiveOverrides[lower]
+        const value = overrides[correctCaseKey]
+        // Remove the key from the overrides so we don't write it again.
+        delete caseInsensitiveOverrides[lower]
+        if (value === "") {
+          // If the value is empty, don't write it. Prevent writing the default
+          // value as well.
+          return
+        }
+        // If the key is in overrides, use the override value.
+        // Doing it this way maintains the default order of the keys.
+        lines.push(this.withIndentation(`${key} ${value}`))
+        return
+      }
       lines.push(this.withIndentation(`${key} ${otherValues[key]}`))
     })
+    // Write remaining overrides that have not been written yet. Sort to maintain deterministic order.
+    const remainingKeys = (Object.keys(caseInsensitiveOverrides) as Array<keyof typeof caseInsensitiveOverrides>).sort()
+    remainingKeys.forEach((key) => {
+      const correctKey = caseInsensitiveOverrides[key]
+      const value = overrides[correctKey]
+      // Only write the value if it is not empty.
+      if (value !== "") {
+        lines.push(this.withIndentation(`${correctKey} ${value}`))
+      }
+    })
+
     lines.push(this.endBlockComment)
     const raw = this.getRaw()
 
diff --git a/yarn.lock b/yarn.lock
index f0a31e41..775f0269 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1418,7 +1418,7 @@ co@3.1.0:
 
 "coder@https://github.com/coder/coder":
   version "0.0.0"
-  resolved "https://github.com/coder/coder#7a1731b6205d9c68f6308ee362ff2d62124b6950"
+  resolved "https://github.com/coder/coder#a6fa8cac582f2fc54eca0191bd54fd43d6d67ac2"
 
 collapse-white-space@^1.0.2:
   version "1.0.6"