diff --git a/apps/canvas/front/src/components/node-app.tsx b/apps/canvas/front/src/components/node-app.tsx
index 15e2fa3..317b7e0 100644
--- a/apps/canvas/front/src/components/node-app.tsx
+++ b/apps/canvas/front/src/components/node-app.tsx
@@ -1,8 +1,19 @@
 import { v4 as uuidv4 } from "uuid";
 import { NodeRect } from "./node-rect";
 import { useStateStore, nodeLabel, AppState, nodeIsConnectable, useEnv, useGithubRepositories } from "@/lib/state";
-import { ServiceNode, ServiceTypes, GatewayHttpsNode, GatewayTCPNode, BoundEnvVar, AppNode, GithubNode } from "config";
-import { KeyboardEvent, FocusEvent, useCallback, useEffect, useMemo, useState } from "react";
+import {
+	ServiceNode,
+	ServiceTypes,
+	GatewayHttpsNode,
+	GatewayTCPNode,
+	BoundEnvVar,
+	AppNode,
+	GithubNode,
+	Machines,
+	Machine,
+	MachinesSchema,
+} from "config";
+import { KeyboardEvent, FocusEvent, useCallback, useEffect, useMemo, useState, useRef } from "react";
 import { z } from "zod";
 import { useForm, EventType, DeepPartial } from "react-hook-form";
 import { zodResolver } from "@hookform/resolvers/zod";
@@ -24,6 +35,9 @@
 import { Gateway } from "@/Gateways";
 import { Port } from "config";
 import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
+import { RadioGroup, RadioGroupItem } from "./ui/radio-group";
+import { useToast } from "@/hooks/use-toast";
+import { LoaderCircle } from "lucide-react";
 
 const sourceSchema = z.object({
 	id: z.string().min(1, "required"),
@@ -33,6 +47,7 @@
 
 const devSchema = z.object({
 	enabled: z.boolean(),
+	mode: z.enum(["VM", "PROXY"]).optional(),
 });
 
 const exposeSchema = z.object({
@@ -45,6 +60,10 @@
 	apiKey: z.string().optional(),
 });
 
+const proxySchema = z.object({
+	address: z.string().min(1, "required"),
+});
+
 const portExposeSchema = z
 	.object({
 		type: z.enum(["https", "tcp"]),
@@ -322,10 +341,16 @@
 
 export function NodeAppDetails({ node, disabled, showName = true, isOverview = false }: NodeDetailsProps<ServiceNode>) {
 	const { data } = node;
+	const defaultTab = useMemo(() => {
+		if (data.dev?.enabled) {
+			return "dev";
+		}
+		return "runtime";
+	}, [data]);
 	return (
 		<>
 			{showName ? <Name node={node} disabled={disabled} /> : null}
-			<Tabs defaultValue="runtime">
+			<Tabs defaultValue={defaultTab}>
 				<TabsList className="w-full flex flex-row justify-between">
 					<TabsTrigger value="runtime">
 						{isOverview ? (
@@ -1157,140 +1182,163 @@
 	);
 }
 
-function Dev({ node, disabled }: { node: ServiceNode; disabled?: boolean }): React.ReactNode {
+function usePrevious<T>(value: T) {
+	const ref = useRef<T>();
+	useEffect(() => {
+		ref.current = value;
+	}, [value]);
+	return ref.current;
+}
+
+function DevVM({ node, disabled }: { node: ServiceNode; disabled?: boolean }): React.ReactNode {
 	const { id, data } = node;
+	const { dev } = data;
+	const prevDev = usePrevious(dev);
 	const env = useEnv();
 	const store = useStateStore();
-	const devForm = useForm<z.infer<typeof devSchema>>({
-		resolver: zodResolver(devSchema),
-		mode: "onChange",
-		defaultValues: {
-			enabled: data.dev ? data.dev.enabled : false,
-		},
-	});
 	useEffect(() => {
-		const sub = devForm.watch((value, { name }) => {
-			if (name === "enabled") {
-				if (value.enabled) {
-					const csGateway: Omit<GatewayHttpsNode, "position"> = {
-						id: uuidv4(),
-						type: "gateway-https",
-						data: {
-							readonly: true,
-							https: {
-								serviceId: id,
-								portId: `${id}-code-server`,
-							},
-							network: data.dev?.expose?.network,
-							subdomain: data.dev?.expose?.subdomain,
-							label: "",
-							envVars: [],
-							ports: [],
-						},
-					};
-					const sshGateway: Omit<GatewayTCPNode, "position"> = {
-						id: uuidv4(),
-						type: "gateway-tcp",
-						data: {
-							readonly: true,
-							exposed: [
-								{
-									serviceId: id,
-									portId: `${id}-ssh`,
-								},
-							],
-							network: data.dev?.expose?.network,
-							subdomain: data.dev?.expose?.subdomain,
-							label: "",
-							envVars: [],
-							ports: [],
-						},
-					};
-					store.addNode(csGateway);
-					store.addNode(sshGateway);
+		console.log("DDDEV", prevDev, dev);
+		if (!dev && !prevDev) {
+			return;
+		}
+		if (
+			dev &&
+			prevDev &&
+			dev.enabled === prevDev.enabled &&
+			"mode" in dev &&
+			"mode" in prevDev &&
+			dev.mode === prevDev.mode
+		) {
+			return;
+		}
+		if (!dev?.enabled || dev.mode !== "VM") {
+			if (prevDev?.enabled && prevDev.mode === "VM") {
+				store.setNodes(
+					store.nodes.filter((n) => n.id !== prevDev.codeServerNodeId && n.id !== prevDev.sshNodeId),
+				);
+				store.setEdges(
+					store.edges.filter((e) => e.target !== prevDev.codeServerNodeId && e.target !== prevDev.sshNodeId),
+				);
+				if (dev?.enabled) {
 					store.updateNodeData<"app">(id, {
 						dev: {
-							enabled: true,
-							expose: data.dev?.expose,
-							codeServerNodeId: csGateway.id,
-							sshNodeId: sshGateway.id,
+							enabled: dev.enabled,
+							mode: dev.mode,
 						},
-						ports: (data.ports || []).concat(
-							{
-								id: `${id}-code-server`,
-								name: "code-server",
-								value: 9090,
-							},
-							{
-								id: `${id}-ssh`,
-								name: "ssh",
-								value: 22,
-							},
-						),
+						ports: (data.ports || []).filter((p) => p.name !== "code-server" && p.name !== "ssh"),
 					});
-					let edges = store.edges.concat([
-						{
-							id: uuidv4(),
-							source: id,
-							sourceHandle: "ports",
-							target: csGateway.id,
-							targetHandle: "https",
-						},
-						{
-							id: uuidv4(),
-							source: id,
-							sourceHandle: "ports",
-							target: sshGateway.id,
-							targetHandle: "tcp",
-						},
-					]);
-					if (data.dev?.expose?.network !== undefined) {
-						edges = edges.concat([
-							{
-								id: uuidv4(),
-								source: csGateway.id,
-								sourceHandle: "subdomain",
-								target: data.dev.expose.network,
-								targetHandle: "subdomain",
-							},
-							{
-								id: uuidv4(),
-								source: sshGateway.id,
-								sourceHandle: "subdomain",
-								target: data.dev.expose.network,
-								targetHandle: "subdomain",
-							},
-						]);
-					}
-					store.setEdges(edges);
 				} else {
-					const { dev } = data;
-					if (dev?.enabled) {
-						store.setNodes(
-							store.nodes.filter((n) => n.id !== dev.codeServerNodeId && n.id !== dev.sshNodeId),
-						);
-						store.setEdges(
-							store.edges.filter((e) => e.target !== dev.codeServerNodeId && e.target !== dev.sshNodeId),
-						);
-					}
 					store.updateNodeData<"app">(id, {
 						dev: {
 							enabled: false,
-							expose: dev?.expose,
 						},
 						ports: (data.ports || []).filter((p) => p.name !== "code-server" && p.name !== "ssh"),
 					});
 				}
 			}
-		});
-		return () => sub.unsubscribe();
-	}, [id, data, devForm, store]);
+		} else {
+			if (!prevDev?.enabled || prevDev.mode !== "VM") {
+				const csGateway: Omit<GatewayHttpsNode, "position"> = {
+					id: uuidv4(),
+					type: "gateway-https",
+					data: {
+						readonly: true,
+						https: {
+							serviceId: id,
+							portId: `${id}-code-server`,
+						},
+						network: dev?.expose?.network,
+						subdomain: dev?.expose?.subdomain,
+						label: "",
+						envVars: [],
+						ports: [],
+					},
+				};
+				const sshGateway: Omit<GatewayTCPNode, "position"> = {
+					id: uuidv4(),
+					type: "gateway-tcp",
+					data: {
+						readonly: true,
+						exposed: [
+							{
+								serviceId: id,
+								portId: `${id}-ssh`,
+							},
+						],
+						network: dev?.expose?.network,
+						subdomain: dev?.expose?.subdomain,
+						label: "",
+						envVars: [],
+						ports: [],
+					},
+				};
+				store.addNode(csGateway);
+				store.addNode(sshGateway);
+				store.updateNodeData<"app">(id, {
+					dev: {
+						enabled: true,
+						mode: "VM",
+						expose: dev?.expose,
+						codeServerNodeId: csGateway.id,
+						sshNodeId: sshGateway.id,
+					},
+					ports: (data.ports || []).concat(
+						{
+							id: `${id}-code-server`,
+							name: "code-server",
+							value: 9090,
+						},
+						{
+							id: `${id}-ssh`,
+							name: "ssh",
+							value: 22,
+						},
+					),
+				});
+				let edges = store.edges.concat([
+					{
+						id: uuidv4(),
+						source: id,
+						sourceHandle: "ports",
+						target: csGateway.id,
+						targetHandle: "https",
+					},
+					{
+						id: uuidv4(),
+						source: id,
+						sourceHandle: "ports",
+						target: sshGateway.id,
+						targetHandle: "tcp",
+					},
+				]);
+				if (dev?.expose?.network !== undefined) {
+					edges = edges.concat([
+						{
+							id: uuidv4(),
+							source: csGateway.id,
+							sourceHandle: "subdomain",
+							target: dev.expose.network,
+							targetHandle: "subdomain",
+						},
+						{
+							id: uuidv4(),
+							source: sshGateway.id,
+							sourceHandle: "subdomain",
+							target: dev.expose.network,
+							targetHandle: "subdomain",
+						},
+					]);
+				}
+				store.setEdges(edges);
+			}
+		}
+	}, [id, data, dev, prevDev, store]);
 	const exposeForm = useForm<z.infer<typeof exposeSchema>>({
 		resolver: zodResolver(exposeSchema),
 		mode: "onChange",
 		defaultValues: {
-			network: data.dev?.expose?.network,
-			subdomain: data.dev?.expose?.subdomain,
+			network: dev && "expose" in dev ? dev.expose?.network : undefined,
+			subdomain: dev && "expose" in dev ? dev.expose?.subdomain : undefined,
 		},
 	});
 	useEffect(() => {
@@ -1300,7 +1348,7 @@
 				{ name }: { name?: keyof z.infer<typeof exposeSchema> | undefined; type?: EventType | undefined },
 			) => {
 				const { dev } = data;
-				if (!dev?.enabled) {
+				if (!dev?.enabled || dev.mode !== "VM") {
 					return;
 				}
 				if (name === "network") {
@@ -1365,31 +1413,12 @@
 			},
 		);
 		return () => sub.unsubscribe();
-	}, [id, data, exposeForm, store]);
+	}, [id, data, dev, prevDev, exposeForm, store]);
+	if (!dev?.enabled || dev.mode !== "VM") {
+		return null;
+	}
 	return (
-		<>
-			<Form {...devForm}>
-				<form className="space-y-2">
-					<FormField
-						control={devForm.control}
-						name="enabled"
-						render={({ field }) => (
-							<FormItem>
-								<div className="flex flex-row gap-1 items-center">
-									<Switch
-										id="devEnabled"
-										onCheckedChange={field.onChange}
-										checked={field.value}
-										disabled={disabled}
-									/>
-									<Label htmlFor="devEnabled">Dev VM</Label>
-								</div>
-								<FormMessage />
-							</FormItem>
-						)}
-					/>
-				</form>
-			</Form>
+		<div>
 			{data.dev && data.dev.enabled && (
 				<Form {...exposeForm}>
 					<form className="space-y-2">
@@ -1438,6 +1467,250 @@
 					</form>
 				</Form>
 			)}
+		</div>
+	);
+}
+
+function DevProxy({ node, disabled }: { node: ServiceNode; disabled?: boolean }): React.ReactNode {
+	const { id, data } = node;
+	const store = useStateStore();
+	const { toast } = useToast();
+	const [machines, setMachines] = useState<Machines>([]);
+	const [loading, setLoading] = useState(false);
+	const [error, setError] = useState<string | null>(null);
+
+	const fetchMachines = useCallback(async () => {
+		setLoading(true);
+		setError(null);
+		try {
+			const response = await fetch("/api/machines", {
+				method: "GET",
+				headers: {
+					"Content-Type": "application/json",
+				},
+			});
+
+			if (!response.ok) {
+				throw new Error(`Failed to fetch machines: ${response.statusText}`);
+			}
+
+			const machinesData = MachinesSchema.safeParse(await response.json());
+			if (machinesData.success) {
+				setMachines(machinesData.data);
+			} else {
+				throw new Error("Invalid machines data");
+			}
+		} catch (err) {
+			const errorMessage = err instanceof Error ? err.message : "Failed to fetch machines";
+			setError(errorMessage);
+			toast({
+				variant: "destructive",
+				title: "Error",
+				description: errorMessage,
+			});
+		} finally {
+			setLoading(false);
+		}
+	}, [toast]);
+
+	useEffect(() => {
+		if (data.dev?.enabled && "mode" in data.dev && data.dev.mode === "PROXY") {
+			fetchMachines();
+		}
+	}, [data.dev, fetchMachines]);
+
+	const proxyForm = useForm<z.infer<typeof proxySchema>>({
+		resolver: zodResolver(proxySchema),
+		mode: "onChange",
+		defaultValues: {
+			address: data.dev && "address" in data.dev ? data.dev.address : undefined,
+		},
+	});
+
+	useEffect(() => {
+		const sub = proxyForm.watch((value, { name }) => {
+			if (name === "address" && value.address) {
+				store.updateNodeData<"app">(id, {
+					dev: {
+						enabled: true,
+						mode: "PROXY",
+						address: value.address,
+					},
+				});
+			}
+		});
+		return () => sub.unsubscribe();
+	}, [id, proxyForm, store]);
+
+	if (!data.dev?.enabled || data.dev.mode !== "PROXY") {
+		return null;
+	}
+	return (
+		<div className="space-y-2">
+			<Form {...proxyForm}>
+				<form className="space-y-2">
+					<FormField
+						control={proxyForm.control}
+						name="address"
+						render={({ field }) => (
+							<FormItem>
+								<Select
+									onValueChange={field.onChange}
+									value={field.value || ""}
+									disabled={disabled || loading}
+								>
+									<FormControl>
+										<SelectTrigger>
+											{loading ? (
+												<div className="flex items-center gap-2">
+													<LoaderCircle className="h-4 w-4 animate-spin" />
+													<span>Loading machines...</span>
+												</div>
+											) : (
+												<SelectValue placeholder="Select a machine" />
+											)}
+										</SelectTrigger>
+									</FormControl>
+									<SelectContent>
+										{loading ? (
+											<div className="flex items-center justify-center p-4">
+												<LoaderCircle className="h-4 w-4 animate-spin" />
+												<span className="ml-2">Loading...</span>
+											</div>
+										) : error ? (
+											<div className="flex flex-col items-center justify-center p-4 text-destructive">
+												<span className="text-sm">Failed to load machines</span>
+												<Button
+													variant="ghost"
+													size="sm"
+													className="mt-2"
+													onClick={fetchMachines}
+												>
+													Retry
+												</Button>
+											</div>
+										) : machines.length === 0 ? (
+											<div className="flex items-center justify-center p-4 text-muted-foreground">
+												<span className="text-sm">No machines available</span>
+											</div>
+										) : (
+											machines.map((machine: Machine) => (
+												<SelectItem key={machine.name} value={machine.name}>
+													{machine.name}
+												</SelectItem>
+											))
+										)}
+									</SelectContent>
+								</Select>
+								<FormMessage />
+							</FormItem>
+						)}
+					/>
+				</form>
+			</Form>
+		</div>
+	);
+}
+
+function Dev({ node, disabled }: { node: ServiceNode; disabled?: boolean }): React.ReactNode {
+	const { id, data } = node;
+	const store = useStateStore();
+	const devForm = useForm<z.infer<typeof devSchema>>({
+		resolver: zodResolver(devSchema),
+		mode: "onChange",
+		defaultValues: {
+			enabled: data.dev ? data.dev.enabled : false,
+			mode: data.dev?.enabled ? data.dev.mode : undefined,
+		},
+	});
+	useEffect(() => {
+		const sub = devForm.watch((value, { name }) => {
+			console.log("DDDEVV", name, value, data.dev);
+			if (name === "enabled") {
+				if (value.enabled) {
+					if (data.dev?.enabled && data.dev.mode === "VM") {
+						return;
+					}
+					store.updateNodeData<"app">(id, {
+						dev: {
+							enabled: true,
+							mode: "VM",
+						},
+					});
+					devForm.setValue("mode", "VM");
+				} else {
+					store.updateNodeData<"app">(id, {
+						dev: {
+							enabled: false,
+						},
+					});
+				}
+			} else if (name === "mode") {
+				if (data.dev?.enabled && data.dev.mode === value.mode) {
+					return;
+				}
+				store.updateNodeData<"app">(id, {
+					dev: {
+						enabled: true,
+						mode: value.mode,
+					},
+				});
+			}
+		});
+		return () => sub.unsubscribe();
+	}, [id, data, devForm, store]);
+	return (
+		<>
+			<Form {...devForm}>
+				<form className="space-y-2">
+					<FormField
+						control={devForm.control}
+						name="enabled"
+						render={({ field }) => (
+							<FormItem>
+								<div className="flex flex-row gap-1 items-center">
+									<Switch
+										id="devEnabled"
+										onCheckedChange={field.onChange}
+										checked={field.value}
+										disabled={disabled}
+									/>
+									<Label htmlFor="devEnabled">Development Mode</Label>
+								</div>
+								<FormMessage />
+							</FormItem>
+						)}
+					/>
+					{data.dev?.enabled && (
+						<FormField
+							control={devForm.control}
+							name="mode"
+							render={({ field }) => (
+								<FormItem>
+									<div className="flex flex-row gap-1 items-center">
+										<RadioGroup
+											onValueChange={field.onChange}
+											value={field.value}
+											disabled={disabled}
+										>
+											<div className="flex items-center space-x-2">
+												<RadioGroupItem value="VM" id="vm" />
+												<Label htmlFor="vm">Create a VM</Label>
+											</div>
+											<div className="flex items-center space-x-2">
+												<RadioGroupItem value="PROXY" id="proxy" />
+												<Label htmlFor="proxy">Proxy to existing machine</Label>
+											</div>
+										</RadioGroup>
+									</div>
+								</FormItem>
+							)}
+						/>
+					)}
+				</form>
+			</Form>
+			<DevVM node={node} disabled={disabled} />
+			<DevProxy node={node} disabled={disabled} />
 		</>
 	);
 }
