{"id":23094,"date":"2025-05-08T06:41:51","date_gmt":"2025-05-08T06:41:51","guid":{"rendered":"https:\/\/viewmyprojects.com\/winwirewp\/?p=23094"},"modified":"2025-07-07T07:12:17","modified_gmt":"2025-07-07T07:12:17","slug":"build-a-virtual-machine-image-for-self-hosted-github-runner-in-azure","status":"publish","type":"post","link":"https:\/\/viewmyprojects.com\/winwirewp\/blog\/build-a-virtual-machine-image-for-self-hosted-github-runner-in-azure\/","title":{"rendered":"10 Steps to Build a Virtual Machine Images for self-hosted GitHub runner in Azure"},"content":{"rendered":"\n<p>If you want to run GitHub Actions on your own virtual machines in Azure, setting up a VM image is a great way to save time and keep things consistent. Instead of repeating the same setup every time, you can build an image once and use it to quickly launch new runners with everything already installed. Managing virtual machines (VMs) efficiently is important in the fast-moving world of cloud computing. It helps with scaling, keeping things consistent, and recovering from issues quickly.<\/p>\n\n\n\n<p id=\"whatvm\">In this guide, we\u2019ll walk through the steps to &#8220;Build a Virtual Machine Image in Azure&#8221; for a self-hosted GitHub runner. This makes it easier to manage your CI\/CD workflows and ensures each runner is ready to go right from the start.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What is Virtual Machine (VM)?<\/strong><\/h2>\n\n\n\n<p>A Virtual Machine is a software-based computer running in the cloud, allowing you to run applications and operating systems without needing physical hardware.<\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li><strong>Purpose:<\/strong> To provide on-demand, scalable, and flexible computing power in the cloud without needing physical hardware.<\/li>\n\n\n\n<li><strong>Uses:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Run applications and services.<\/li>\n\n\n\n<li>Host websites or databases.<\/li>\n\n\n\n<li>Test and develop software in isolated environments.<\/li>\n\n\n\n<li>Support enterprise workloads and remote desktops.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"azurevm\"><strong>What is an Azure Virtual Machine (VM) Image?<\/strong><\/h2>\n\n\n\n<p>An Azure VM image is a template containing the OS, applications, configurations, and data needed to create a new VM. It ensures consistency and simplifies the deployment of VMs across different environments. A VM Image is a pre-configured template that contains an operating system and software, which you can use to quickly create new virtual machines with the same setup.<\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li><strong>Purpose:<\/strong> To provide a pre-configured template that helps quickly create new VMs with the same setup and configuration.<\/li>\n\n\n\n<li><strong>Uses:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Deploy consistent and repeatable VM configurations.<\/li>\n\n\n\n<li>Create custom VMs with specific software and settings.<\/li>\n\n\n\n<li>Quickly scale environments by using saved VM images.<\/li>\n\n\n\n<li>Save time on setup by using pre-configured OS and application templates.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"self\"><a><strong>Uses of Self-Hosted GitHub Runner<\/strong><\/a><\/h2>\n\n\n\n<p>Before diving into the technical details, let\u2019s understand why you might need a self-hosted runner:<\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li><strong>Cost Efficiency:<\/strong> Especially for large-scale projects, self-hosted runners can reduce costs compared to using GitHub-hosted runners.<\/li>\n\n\n\n<li><strong>Custom Environment:<\/strong> Install custom dependencies, tools, and configurations tailored to your project.<\/li>\n\n\n\n<li><strong>Performance:<\/strong> Dedicated resources mean faster build times, especially for resource-intensive jobs.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Prerequisites<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li>An active Azure subscription.<\/li>\n\n\n\n<li>Access to Azure Portal or Azure CLI.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step1\"><strong>10 Steps to Build a Virtual Machine Image for self-hosted GitHub runner in Azure<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 1: Understand the Basics<\/strong><\/h3>\n\n\n\n<p>A GitHub runner is a server (virtual or physical) that runs your GitHub Actions workflows. You can host it on:<\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li>Your local machine<\/li>\n\n\n\n<li>A virtual machine (VM) in the cloud (like AWS, Azure, GCP)<\/li>\n\n\n\n<li>A dedicated server<\/li>\n<\/ul>\n\n\n\n<p>In this guide, we&#8217;ll focus on setting up a VM image to make deploying the runner easier.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step2\"><strong>Step 2: Choose Your Virtualization Platform<\/strong><\/h3>\n\n\n\n<p>You can create VM images using platforms like:<\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li><strong>VirtualBox (for local VMs)<\/strong><\/li>\n\n\n\n<li><strong>VMware<\/strong><\/li>\n\n\n\n<li><strong>Cloud platforms (AWS, Azure, Google Cloud)<\/strong><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step3\"><strong>Step 3: Create a Virtual Machine in Azure<\/strong><\/h3>\n\n\n\n<ol style=\"list-style-type:lower-alpha\" class=\"wp-block-list blog-detail-list\">\n<li><strong>Log into the Azure Portal<\/strong>: Open <a href=\"https:\/\/portal.azure.com\/\" target=\"_blank\" rel=\"noopener\">Azure Portal<\/a> and log into your Azure account.<\/li>\n\n\n\n<li><strong>Create a VM<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Navigate to <strong>&#8220;Virtual machines&#8221;<\/strong> from the left-side menu.<\/li>\n\n\n\n<li>Click <strong>&#8220;Create&#8221;<\/strong> to start creating a new VM.<\/li>\n\n\n\n<li>Select a subscription and resource group *.<\/li>\n\n\n\n<li>Fill in the basic details for your VM, such as:\n<ul class=\"wp-block-list\">\n<li><strong>VM Name*<\/strong><\/li>\n\n\n\n<li><strong>Region*<\/strong><\/li>\n\n\n\n<li><strong>Availability options<\/strong><\/li>\n\n\n\n<li><strong>Zone Options<\/strong>: Self-selected zone\/ Azure-selected zone (Preview)<\/li>\n\n\n\n<li><strong>Availability Zone*<\/strong>: Zone 1\/ Zone 2\/ Zone 3<\/li>\n\n\n\n<li><strong>Security Type<\/strong>: Standard\/ Trusted Launch VM\/ Confidential VM<\/li>\n\n\n\n<li><strong>Image*<\/strong>: Choose a base image (e.g., Windows Server 2022, Ubuntu 20.04).<\/li>\n\n\n\n<li><strong>Size*<\/strong>: Choose an appropriate size based on your requirements.<\/li>\n\n\n\n<li><strong>Administrator Account<\/strong>: Create a username and password or SSH key for login.<\/li>\n\n\n\n<li><strong>Authentication Type:<\/strong> SSH public key\/ Password<\/li>\n\n\n\n<li><strong>Username*<\/strong>: default name: azureuser<\/li>\n\n\n\n<li><strong>SSH public key source:<\/strong> Generate new key pair\/ Use existing key stored in Azure\/ Use existing public key<\/li>\n\n\n\n<li><strong>SSH key type*:<\/strong> RSA SSH Format\/ Ed25519 SSH Format<\/li>\n\n\n\n<li><strong>Key pair name:&nbsp;<\/strong><\/li>\n\n\n\n<li><strong>Public Inbound Ports*: <\/strong>None\/ Allow Selected Port<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Click <strong>Next<\/strong> to go through the configuration steps (disks, networking, etc.) and <strong>Review + Create<\/strong>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Configure the VM:<\/strong>\n<ul class=\"wp-block-list\">\n<li>CPU: 2 or more cores<\/li>\n\n\n\n<li>RAM: 4GB or more<\/li>\n\n\n\n<li>Storage: At least 20GB<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Connect to Your VM<\/strong>:\n<ul class=\"wp-block-list\">\n<li>After the VM is deployed, click on the <strong>&#8220;Connect&#8221;<\/strong> button to either use <strong>RDP<\/strong> (Windows) or <strong>SSH<\/strong> (Linux) to connect to the VM.<\/li>\n\n\n\n<li>Click on Download RDP File. Connect the RDP using the credentials you set earlier to log into the VM.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step4\"><strong>Step 4: Prepare the VM for Image Creation<\/strong><\/h3>\n\n\n\n<p>Before creating an image, you should ensure that the VM is in a &#8220;generalized&#8221; state (this means that it\u2019s ready to be reused as a template). Connect to VM and follow the below steps.<\/p>\n\n\n\n<p><strong>a. Install Necessary Software:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li>Install and configure all the software and settings you want in the image.<\/li>\n\n\n\n<li>For Windows (<strong>Windows x64<\/strong>): Download the Windows runner from <a href=\"https:\/\/github.com\/actions\/runner\/releases\" target=\"_blank\" rel=\"noopener\">GitHub Runner Releases<\/a>.<\/li>\n\n\n\n<li>Set up any configurations, network settings, applications, etc.<\/li>\n\n\n\n<li><strong>Install Required Software<\/strong>: GitHub runners need some basic tools:\n<ul class=\"wp-block-list\">\n<li><strong>Install Git:&nbsp; <\/strong>sudo apt install git -y<\/li>\n\n\n\n<li><strong>Install Dependencies: <\/strong>sudo apt install curl jq -y<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step5\"><strong>Step 5: Set Up the GitHub Runner<\/strong><\/h3>\n\n\n\n<p>Once your virtual machine has an OS installed, follow these steps to configure it as a GitHub runner:<\/p>\n\n\n\n<ol class=\"wp-block-list blog-detail-list\">\n<li><strong>Install Dependencies:<\/strong><\/li>\n<\/ol>\n\n\n\n<p>         sudo apt-get update<\/p>\n\n\n\n<p>         sudo apt-get install -y curl sudo lsb-release<\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li><a><strong>Create a User for the GitHub Runner:<\/strong><\/a><\/li>\n<\/ul>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;It&#8217;s a good practice to run the GitHub runner under a separate user.<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sudo useradd -m -s \/bin\/bash github-runner<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sudo su &#8211; github-runner<\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li><strong>Go to GitHub Repository:<\/strong><\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><\/li>\n<\/ol>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Navigate to your repo <strong>\u2192 Settings \u2192 Actions \u2192 Runners \u2192 New self-hosted runner<\/strong>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"989\" height=\"386\" src=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image.png\" alt=\"Build a Virtual Machine Image\" class=\"wp-image-23109\" style=\"width:719px;height:auto\" srcset=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image.png 989w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-300x117.png 300w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-768x300.png 768w\" sizes=\"auto, (max-width: 989px) 100vw, 989px\" \/><\/figure><\/div>\n\n\n<p>Select Windows option as shown below. You will use the token when setting up the runner on your machine and copy the provided commands. Open PowerShell in admin mode and execute all the commands mentioned in <strong>Download<\/strong> and C<strong>onfigure<\/strong> section when you select Windows as shown above.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"253\" src=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-1-1024x253.png\" alt=\"Build a Virtual Machine Image\" class=\"wp-image-23110\" style=\"width:806px;height:auto\" srcset=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-1-1024x253.png 1024w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-1-300x74.png 300w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-1-768x189.png 768w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-1.png 1034w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Run the Commands in Your VM:<\/strong><\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"281\" src=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-2-1024x281.png\" alt=\"github runner\" class=\"wp-image-23111\" style=\"width:488px;height:auto\" srcset=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-2-1024x281.png 1024w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-2-300x82.png 300w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-2-768x211.png 768w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-2.png 1180w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Install GitHub Runner as a Service (Optional)<\/strong><\/li>\n<\/ul>\n\n\n\n<p>If you want your GitHub Actions runner to be always active and start automatically on boot:<\/p>\n\n\n\n<p><strong>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For Ubuntu\/Debian:<\/strong><\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sudo .\/svc.sh install<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sudo .\/svc.sh start<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This installs and starts the runner as a system service, ensuring that it runs continuously.<\/p>\n\n\n\n<p><strong>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For Windows:<\/strong><\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; You can use the built-in nssm (Non-Sucking Service Manager) to install the runner as a service on Windows.<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Execute the following(powershell):<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nssm install GitHubActionsRunner &#8220;C:\\path\\to\\config.cmd&#8221;<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>Start the Runner:<\/strong><\/p>\n\n\n\n<p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/strong>.\/run.sh<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The runner will now be active and ready to execute jobs.<\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li><strong>De-allocate the VM (Optional but recommended):<\/strong><\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li>In some cases, it\u2019s recommended to deallocate the VM before creating an image.<\/li>\n\n\n\n<li>Deallocation ensures that the VM is stopped, but the resources are not deleted, which is ideal when preparing to capture the image.<\/li>\n<\/ul>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; You can deallocate the VM from the Azure Portal or use Azure CLI or PowerShell.<\/p>\n\n\n\n<p><strong>Azure CLI: <\/strong>Open Azure Cloud Shell in the portal. Navigate to CLI\/PS tab and look for deallocate command then click on <strong>Run<\/strong>.<\/p>\n\n\n\n<p><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"169\" src=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-3-1024x169.png\" alt=\"github runner\" class=\"wp-image-23112\" style=\"width:863px;height:auto\" srcset=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-3-1024x169.png 1024w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-3-300x50.png 300w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-3-768x127.png 768w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-3.png 1034w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"940\" height=\"524\" src=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-4.png\" alt=\"Azure CLI\" class=\"wp-image-23113\" style=\"width:850px;height:auto\" srcset=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-4.png 940w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-4-300x167.png 300w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-4-768x428.png 768w\" sizes=\"auto, (max-width: 940px) 100vw, 940px\" \/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"step6\"><strong>Step 6: Generalize the VM (Prepare for Image Capture)<\/strong><\/h3>\n\n\n\n<p>To create a reusable VM image, the VM must be generalized using the Sysprep tool (on Windows) or cloud-init (on Linux).<\/p>\n\n\n\n<p><strong>For Windows VM<\/strong>:<\/p>\n\n\n\n<p>\u00b7 Connect to the VM via RDP.<\/p>\n\n\n\n<p>\u00b7 Open <strong>Command Prompt<\/strong> as Administrator. Change directory to the <strong>sysprep<\/strong> folder:<\/p>\n\n\n\n<p>&nbsp;&nbsp; cd C:\\Windows\\System32\\sysprep<\/p>\n\n\n\n<p>\u00b7 Run the following command:<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysprep.exe \/oobe \/generalize \/shutdown<\/p>\n\n\n\n<p>The \/oobe flag will bring the machine to the &#8220;Out-Of-Box-Experience&#8221; mode, and \/generalize removes machine-specific information. The VM will shut down after the process completes.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step7\"><strong>Step 7: Capture the Image of the VM<\/strong><\/h3>\n\n\n\n<p>Once the VM is generalized, it\u2019s ready to be captured as an image.<\/p>\n\n\n\n<ol class=\"wp-block-list blog-detail-list\">\n<li><strong>Go to the Azure Portal<\/strong> and find your VM.<\/li>\n\n\n\n<li>In the VM&#8217;s settings page, under the <strong>&#8220;Overview&#8221;<\/strong> section, click on <strong>&#8220;Capture&#8221;<\/strong>.<\/li>\n\n\n\n<li>Fill in the image details:\n<ul class=\"wp-block-list\">\n<li><strong>Image Name<\/strong>: Give the image a name.<\/li>\n\n\n\n<li><strong>Resource Group<\/strong>: Select or create a resource group where the image will be stored.<\/li>\n\n\n\n<li><strong>Create a new image<\/strong>: Confirm that you want to create a new image from the generalized VM.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"901\" height=\"91\" src=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-5.png\" alt=\"Azure Compute Gallery\" class=\"wp-image-23114\" style=\"width:763px;height:auto\" srcset=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-5.png 901w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-5-300x30.png 300w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-5-768x78.png 768w\" sizes=\"auto, (max-width: 901px) 100vw, 901px\" \/><\/figure><\/div>\n\n\n<ol style=\"list-style-type:lower-roman\" class=\"wp-block-list blog-detail-list\">\n<li>The first option will create the VM Image version which is a specific version of a VM image. It helps you manage different iterations or versions of an image over time, especially when you make updates or changes to the image. Each version refers to a specific state or version of a VM image, typically associated with changes in the VM\u2019s OS, software updates, or configurations.<\/li>\n\n\n\n<li>Second option will create the VM Image. A <strong>VM Image<\/strong> is a template or a snapshot of a virtual machine (VM) that contains the OS and software installed on the VM. It is used to create new VMs that are identical to the original VM from which the image was created. It includes the operating system, installed applications, system settings, and other configurations of the VM.<\/li>\n<\/ol>\n\n\n\n<p>Click <strong>&#8220;Create&#8221;<\/strong>. This process will create a custom image from your VM that you can deploy later to new VMs.<\/p>\n\n\n\n<p><strong>Note: <\/strong>Please note that when you create a VM image version, it will not appear under the &#8220;My Images&#8221; section while creating a VM, nor on the &#8220;Images&#8221; page. Instead, it can be found on the Azure Compute Galleries page.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step8\"><strong>Step 8: Verify the Image and Deploy New VMs from It<\/strong><\/h3>\n\n\n\n<p>After the image is created, it will be available in the <strong>&#8220;Images&#8221;<\/strong> section of the Azure portal, under your resource group.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"208\" src=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-6-1024x208.png\" alt=\"Azure Virtual Machine\" class=\"wp-image-23115\" style=\"width:734px;height:auto\" srcset=\"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-6-1024x208.png 1024w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-6-300x61.png 300w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-6-768x156.png 768w, https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/image-6.png 1034w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<p>To deploy a new VM from this custom image:<\/p>\n\n\n\n<ul class=\"wp-block-list blog-detail-list\">\n<li>Go to <strong>&#8220;Virtual Machines&#8221;<\/strong> in the Azure portal.<\/li>\n\n\n\n<li>Click <strong>&#8220;Add&#8221;<\/strong> to create a new VM.<\/li>\n\n\n\n<li>Under the <strong>&#8220;Image&#8221;<\/strong> section, select <strong>&#8220;My Images&#8221;<\/strong> and choose the custom image you just created.<\/li>\n\n\n\n<li>Proceed with the VM creation process as usual.<\/li>\n\n\n\n<li>Click on <strong>+ Create VM<\/strong> to deploy a new virtual machine from the captured image.<\/li>\n\n\n\n<li>Follow the usual VM creation process (select the VM size, network settings, etc.).<\/li>\n\n\n\n<li>When you get to the <strong>Disks<\/strong> section, select <strong>Use an existing image<\/strong> and choose the image you captured.<\/li>\n\n\n\n<li>Complete the creation process to deploy the VM.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step9\"><strong>Step 9: Store the Image in Azure<\/strong><\/h3>\n\n\n\n<p>Once the image is captured, it is stored as an <strong>Azure Managed Image<\/strong> by default. You can use this image to create new VMs. The image itself is stored as a resource in <strong>Azure Storage<\/strong> or as a <strong>Managed Image<\/strong> within an Azure <strong>Resource Group<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step10\"><strong>Step 10: Clean Up Resources (Optional)<\/strong><\/h3>\n\n\n\n<p>After successfully capturing the image and using it for new VMs, you may want to clean up resources to avoid unnecessary costs.<\/p>\n\n\n\n<p><strong>1. Delete the Source VM (if not needed anymore).<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><\/li>\n<\/ol>\n\n\n\n<ol start=\"1\" class=\"wp-block-list blog-detail-list\">\n<li><\/li>\n<\/ol>\n\n\n\n<p>If you didn&#8217;t delete the VM during the image capture, you can delete it manually from the portal or using the Azure CLI:<\/p>\n\n\n\n<p><strong>az vm delete &#8211;resource-group &lt;ResourceGroupName&gt; &#8211;name &lt;VMName&gt; &#8211;yes<\/strong><\/p>\n\n\n\n<p><strong>2. Delete Unused Storage\/Disk (if not used).<\/strong><\/p>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><\/li>\n<\/ol>\n\n\n\n<p>If you are storing the image in blob storage, the original disk may no longer be necessary after image capture. You can delete it:<\/p>\n\n\n\n<p><strong>az disk delete &#8211;resource-group &lt;ResourceGroupName&gt; &#8211;name &lt;DiskName&gt; &#8211;yes<\/strong><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Conclusion<\/strong><\/p>\n\n\n\n<p>Creating a VM image for a self-hosted <a href=\"https:\/\/viewmyprojects.com\/winwirewp\/blog\/github-actions\/\">GitHub<\/a> runner in Azure helps streamline your CI\/CD setup by providing a ready-to-use, consistent environment. Once the image is built, you can quickly spin up new runners without repeating the installation and configuration steps. This not only saves time but also reduces the risk of errors. Whether you&#8217;re managing a small project or a large pipeline, this approach brings reliability and efficiency to your DevOps workflow.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you want to run GitHub Actions on your own virtual machines in Azure, setting up a VM image is a great way to save time and keep things consistent. Instead of repeating the same setup every time, you can build an image once and use it to quickly launch new runners with everything already&hellip; <a class=\"more-link\" href=\"https:\/\/viewmyprojects.com\/winwirewp\/blog\/build-a-virtual-machine-image-for-self-hosted-github-runner-in-azure\/\">Continue reading <span class=\"screen-reader-text\">10 Steps to Build a Virtual Machine Images for self-hosted GitHub runner in Azure<\/span><\/a><\/p>\n","protected":false},"author":145,"featured_media":23129,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_eb_attr":"","_uag_custom_page_level_css":"","footnotes":""},"categories":[61,62,59,1],"tags":[],"class_list":["post-23094","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-app-modernization-blogs","category-cloud-blogs","category-blogs","category-uncategorized","entry"],"acf":[],"featured_image_src":"https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/Build-a-Virtual-Machine-Image.webp","author_info":{"display_name":"Anjali","author_link":"https:\/\/viewmyprojects.com\/winwirewp\/author\/anjali\/"},"views":1171,"uagb_featured_image_src":{"full":["https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/Build-a-Virtual-Machine-Image.webp",1667,917,false],"thumbnail":["https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/Build-a-Virtual-Machine-Image-150x150.webp",150,150,true],"medium":["https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/Build-a-Virtual-Machine-Image-300x165.webp",300,165,true],"medium_large":["https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/Build-a-Virtual-Machine-Image-768x422.webp",750,412,true],"large":["https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/Build-a-Virtual-Machine-Image-1024x563.webp",750,412,true],"1536x1536":["https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/Build-a-Virtual-Machine-Image-1536x845.webp",1536,845,true],"2048x2048":["https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/Build-a-Virtual-Machine-Image.webp",1667,917,false],"post-thumbnail":["https:\/\/viewmyprojects.com\/winwirewp\/wp-content\/uploads\/2025\/05\/Build-a-Virtual-Machine-Image-1568x863.webp",1568,863,true]},"uagb_author_info":{"display_name":"Anjali","author_link":"https:\/\/viewmyprojects.com\/winwirewp\/author\/anjali\/"},"uagb_comment_info":0,"uagb_excerpt":"If you want to run GitHub Actions on your own virtual machines in Azure, setting up a VM image is a great way to save time and keep things consistent. Instead of repeating the same setup every time, you can build an image once and use it to quickly launch new runners with everything already&hellip;&hellip;","_links":{"self":[{"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/posts\/23094","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/users\/145"}],"replies":[{"embeddable":true,"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/comments?post=23094"}],"version-history":[{"count":10,"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/posts\/23094\/revisions"}],"predecessor-version":[{"id":23572,"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/posts\/23094\/revisions\/23572"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/media\/23129"}],"wp:attachment":[{"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/media?parent=23094"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/categories?post=23094"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/viewmyprojects.com\/winwirewp\/wp-json\/wp\/v2\/tags?post=23094"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}