diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ca1e0f1d04..3b922cf301 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -3,13 +3,13 @@ name: Build job
 on:
   workflow_dispatch:
     inputs: {}
-  push:
-    branches: [ master ]
-    paths-ignore:
-      - '.github/*'
-      - '.github/ISSUE_TEMPLATE/**'
-      - '*.yml'
-      - 'README.md'
+  #push:
+  #  branches: [ master ]
+  #  paths-ignore:
+  #    - '.github/*'
+  #    - '.github/ISSUE_TEMPLATE/**'
+  #    - '*.yml'
+  #    - 'README.md'
   pull_request:
     branches: [ master ]
     paths-ignore:
@@ -59,14 +59,14 @@ jobs:
       - name: Clear
         run: dotnet clean && dotnet nuget locals all --clear
       - name: Build
-        run: dotnet build -c "${{ matrix.configuration }}" /p:Version="1.0.0" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
+        run: dotnet build -c "${{ matrix.configuration }}" /p:Version="1.1.0" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
       - name: Test
         run: dotnet test -c "${{ matrix.configuration }}"
       - name: Publish Ryujinx
-        run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
+        run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="1.1.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
         if: github.event_name == 'pull_request'
       - name: Publish Ryujinx.Headless.SDL2
-        run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
+        run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="1.1.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
         if: github.event_name == 'pull_request'
       - name: Upload Ryujinx artifact
         uses: actions/upload-artifact@v2
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000000..189a17cd94
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,95 @@
+name: Release job
+
+on:
+  workflow_dispatch:
+    inputs: {}
+  push:
+    branches: [ master ]
+    paths-ignore:
+      - '.github/*'
+      - '.github/ISSUE_TEMPLATE/**'
+      - '*.yml'
+      - 'README.md'
+
+
+jobs:
+  release:
+    runs-on: windows-latest
+
+    env:
+      POWERSHELL_TELEMETRY_OPTOUT: 1
+      DOTNET_CLI_TELEMETRY_OPTOUT: 1
+      RYUJINX_BASE_VERSION: "1.1"
+      RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
+      RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
+      RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions/setup-dotnet@v1
+        with:
+          dotnet-version: 6.0.x
+      - name: Ensure NuGet Source
+        uses: fabriciomurta/ensure-nuget-source@v1
+      - name: Clear
+        run: dotnet clean && dotnet nuget locals all --clear
+      - name: Get version info
+        id: version_info
+        run: |
+          echo "::set-output name=build_version::${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} + 7181))"
+          echo "::set-output name=git_short_hash::$(git rev-parse --short "${{ github.sha }}")"
+        shell: bash
+      - name: Configure for release
+        run: |
+          sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformations.cs
+          sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformations.cs
+          sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformations.cs
+          sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformations.cs
+          sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformations.cs
+        shell: bash
+      - name: Create output dir
+        run: "mkdir release_output"
+      - name: Publish Windows
+        run: |
+          dotnet publish -c Release -r win-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
+          dotnet publish -c Release -r win-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
+      - name: Packing Windows builds
+        run: |
+          pushd publish_windows
+          7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
+          popd
+
+          pushd publish_windows_sdl2_headless
+          7z a ../release_output/ryujinx-headless-sdl2-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
+          popd
+        shell: bash
+
+      - name: Publish Linux
+        run: |
+          dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
+          dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
+
+      - name: Packing Linux builds
+        run: |
+          pushd publish_linux
+          tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
+          popd
+
+          pushd publish_linux_sdl2_headless
+          tar -czvf ../release_output/ryujinx-headless-sdl2-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
+          popd
+        shell: bash
+
+      - name: Pushing new release
+        uses: ncipollo/release-action@v1
+        with:
+          name: ${{ steps.version_info.outputs.build_version }}
+          artifacts: "release_output/*.tar.gz,release_output/*.zip"
+          tag: ${{ steps.version_info.outputs.build_version }}
+          body: "For more informations about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
+          allowUpdates: true
+          removeArtifacts: true
+          replacesArtifacts: true
+          owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
+          repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
+          token: ${{ secrets.RELEASE_TOKEN }}
diff --git a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
index 5591c60b4a..e83b26cdb3 100644
--- a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
@@ -30,7 +30,7 @@ namespace Ryujinx.Common.Logging
                 files[i].Delete();
             }
 
-            string version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
+            string version = ReleaseInformations.GetVersion();
 
             // Get path for the current time
             path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.log");
diff --git a/Ryujinx.Common/ReleaseInformations.cs b/Ryujinx.Common/ReleaseInformations.cs
new file mode 100644
index 0000000000..1dcb4bf303
--- /dev/null
+++ b/Ryujinx.Common/ReleaseInformations.cs
@@ -0,0 +1,32 @@
+namespace Ryujinx.Common
+{
+    // DO NOT EDIT, filled by CI
+    public static class ReleaseInformations
+    {
+        public static string BuildVersion = "%%RYUJINX_BUILD_VERSION%%";
+        public static string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
+        public static string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%";
+        public static string ReleaseChannelOwner = "%%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER%%";
+        public static string ReleaseChannelRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_REPO%%";
+
+        public static bool IsValid()
+        {
+            return !BuildGitHash.StartsWith("%%") &&
+                   !ReleaseChannelName.StartsWith("%%") &&
+                   !ReleaseChannelOwner.StartsWith("%%") &&
+                   !ReleaseChannelRepo.StartsWith("%%");
+        }
+
+        public static string GetVersion()
+        {
+            if (IsValid())
+            {
+                return BuildVersion;
+            }
+            else
+            {
+                return "1.0.0-dirty";
+            }
+        }
+    }
+}
diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs
index 71487a0993..14c7236035 100644
--- a/Ryujinx.Headless.SDL2/Program.cs
+++ b/Ryujinx.Headless.SDL2/Program.cs
@@ -3,6 +3,7 @@ using ARMeilleure.Translation.PTC;
 using CommandLine;
 using LibHac.Tools.FsSystem;
 using Ryujinx.Audio.Backends.SDL2;
+using Ryujinx.Common;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.Common.Configuration.Hid.Controller;
@@ -57,7 +58,7 @@ namespace Ryujinx.Headless.SDL2
 
         static void Main(string[] args)
         {
-            Version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
+            Version = ReleaseInformations.GetVersion();
 
             Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";
 
diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs
index 5ce896e5e3..4a96208faa 100644
--- a/Ryujinx/Modules/Updater/Updater.cs
+++ b/Ryujinx/Modules/Updater/Updater.cs
@@ -3,6 +3,7 @@ using ICSharpCode.SharpZipLib.GZip;
 using ICSharpCode.SharpZipLib.Tar;
 using ICSharpCode.SharpZipLib.Zip;
 using Newtonsoft.Json.Linq;
+using Ryujinx.Common;
 using Ryujinx.Common.Logging;
 using Ryujinx.Ui;
 using Ryujinx.Ui.Widgets;
@@ -29,17 +30,26 @@ namespace Ryujinx.Modules
         private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish");
         private static readonly int    ConnectionCount  = 4;
 
-        private static string _jobId;
         private static string _buildVer;
         private static string _platformExt;
         private static string _buildUrl;
         private static long   _buildSize;
-        
-        private const string AppveyorApiUrl = "https://ci.appveyor.com/api";
+
+        private const string GitHubApiURL = "https://api.github.com";
 
         // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates.
         private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" };
 
+        private static HttpClient ConstructHttpClient()
+        {
+            HttpClient result = new HttpClient();
+
+            // Required by GitHub to interract with APIs.
+            result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
+
+            return result;
+        }
+
         public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate)
         {
             if (Running) return;
@@ -88,22 +98,45 @@ namespace Ryujinx.Modules
                 return;
             }
 
-            // Get latest version number from Appveyor
+            // Get latest version number from GitHub API
             try
             {
-                using (HttpClient jsonClient = new HttpClient())
+                using (HttpClient jsonClient = ConstructHttpClient())
                 {
+                    string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformations.ReleaseChannelOwner}/{ReleaseInformations.ReleaseChannelRepo}/releases/latest";
+
                     // Fetch latest build information
-                    string  fetchedJson = await jsonClient.GetStringAsync($"{AppveyorApiUrl}/projects/gdkchan/ryujinx/branch/master");
+                    string  fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
                     JObject jsonRoot    = JObject.Parse(fetchedJson);
-                    JToken  buildToken  = jsonRoot["build"];
+                    JToken  assets      = jsonRoot["assets"];
 
-                    _jobId    = (string)buildToken["jobs"][0]["jobId"];
-                    _buildVer = (string)buildToken["version"];
-                    _buildUrl = $"{AppveyorApiUrl}/buildjobs/{_jobId}/artifacts/ryujinx-{_buildVer}-{_platformExt}";
+                    _buildVer = (string)jsonRoot["name"];
 
-                    // If build not done, assume no new update are availaible.
-                    if ((string)buildToken["jobs"][0]["status"] != "success")
+                    foreach (JToken asset in assets)
+                    {
+                        string assetName = (string)asset["name"];
+                        string assetState = (string)asset["state"];
+                        string downloadURL = (string)asset["browser_download_url"];
+
+                        if (!assetName.StartsWith("ryujinx-headless-sdl2") && assetName.EndsWith(_platformExt))
+                        {
+                            _buildUrl = downloadURL;
+
+                            if (assetState != "uploaded")
+                            {
+                                if (showVersionUpToDate)
+                                {
+                                    GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", "");
+                                }
+
+                                return;
+                            }
+
+                            break;
+                        }
+                    }
+
+                    if (_buildUrl == null)
                     {
                         if (showVersionUpToDate)
                         {
@@ -117,7 +150,7 @@ namespace Ryujinx.Modules
             catch (Exception exception)
             {
                 Logger.Error?.Print(LogClass.Application, exception.Message);
-                GtkDialog.CreateErrorDialog("An error occurred when trying to get release information from AppVeyor. This can be caused if a new release is being compiled by AppVeyor. Try again in a few minutes.");
+                GtkDialog.CreateErrorDialog("An error occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes.");
 
                 return;
             }
@@ -128,8 +161,8 @@ namespace Ryujinx.Modules
             }
             catch
             {
-                GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from AppVeyor.", "Cancelling Update!");
-                Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from AppVeyor!");
+                GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from GitHub Release.", "Cancelling Update!");
+                Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from GitHub Release!");
 
                 return;
             }
@@ -148,7 +181,7 @@ namespace Ryujinx.Modules
             }
 
             // Fetch build size information to learn chunk sizes.
-            using (HttpClient buildSizeClient = new HttpClient())
+            using (HttpClient buildSizeClient = ConstructHttpClient())
             {
                 try
                 {
@@ -520,7 +553,7 @@ namespace Ryujinx.Modules
                 return false;
             }
 
-            if (Program.Version.Contains("dirty"))
+            if (Program.Version.Contains("dirty") || !ReleaseInformations.IsValid())
             {
                 if (showWarnings)
                 {
diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs
index 1e0fdd3afd..8cd5a9969f 100644
--- a/Ryujinx/Program.cs
+++ b/Ryujinx/Program.cs
@@ -1,5 +1,6 @@
 using ARMeilleure.Translation.PTC;
 using Gtk;
+using Ryujinx.Common;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.GraphicsDriver;
 using Ryujinx.Common.Logging;
@@ -68,7 +69,7 @@ namespace Ryujinx
             // Delete backup files after updating.
             Task.Run(Updater.CleanupUpdate);
 
-            Version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
+            Version = ReleaseInformations.GetVersion();
 
             Console.Title = $"Ryujinx Console {Version}";