diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs
index 832673703b..8bdeadae5c 100644
--- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs
@@ -5,6 +5,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using osu.Framework.Allocation;
 using osu.Framework.Graphics;
+using osu.Framework.Screens;
 using osu.Game.Beatmaps;
 using osu.Game.Graphics.UserInterface;
 using osu.Game.Scoring;
@@ -26,8 +27,14 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
         private TriangleButton undeleteButton;
 
         [BackgroundDependencyLoader]
-        private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, DialogOverlay dialogOverlay)
+        private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, DialogOverlay dialogOverlay, OsuGame game)
         {
+            Add(importBeatmapsButton = new SettingsButton
+            {
+                Text = "Migrate storage to new location",
+                Action = () => game.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen()))
+            });
+
             if (beatmaps.SupportsImportFromStable)
             {
                 Add(importBeatmapsButton = new SettingsButton
diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs
new file mode 100644
index 0000000000..76f01dc4b9
--- /dev/null
+++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs
@@ -0,0 +1,88 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.IO;
+using System.Threading.Tasks;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Logging;
+using osu.Framework.Screens;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Screens;
+
+namespace osu.Game.Overlays.Settings.Sections.Maintenance
+{
+    public class MigrationRunScreen : OsuScreen
+    {
+        private readonly DirectoryInfo destination;
+
+        [Resolved]
+        private OsuGame game { get; set; }
+
+        public override bool AllowBackButton => false;
+
+        public override bool AllowExternalScreenChange => false;
+
+        public override bool DisallowExternalBeatmapRulesetChanges => true;
+
+        private Task migrationTask;
+
+        public MigrationRunScreen(DirectoryInfo destination)
+        {
+            this.destination = destination;
+        }
+
+        protected override void LoadComplete()
+        {
+            base.LoadComplete();
+
+            InternalChildren = new Drawable[]
+            {
+                new FillFlowContainer
+                {
+                    AutoSizeAxes = Axes.Both,
+                    Direction = FillDirection.Vertical,
+                    Anchor = Anchor.Centre,
+                    Origin = Anchor.Centre,
+                    Children = new Drawable[]
+                    {
+                        new OsuSpriteText
+                        {
+                            Anchor = Anchor.Centre,
+                            Origin = Anchor.Centre,
+                            Text = "Migration in progress",
+                            Font = OsuFont.Default.With(size: 48)
+                        },
+                        new LoadingSpinner(true)
+                        {
+                            State = { Value = Visibility.Visible }
+                        }
+                    }
+                },
+            };
+
+            Beatmap.Value = Beatmap.Default;
+
+            migrationTask = Task.Run(() => game.Migrate(destination.FullName))
+                                .ContinueWith(t =>
+                                {
+                                    if (t.IsFaulted)
+                                        Logger.Log($"Error during migration: {t.Exception?.Message}", level: LogLevel.Error);
+
+                                    Schedule(this.Exit);
+                                });
+        }
+
+        public override bool OnExiting(IScreen next)
+        {
+            // block until migration is finished
+            if (migrationTask?.IsCompleted == false)
+                return true;
+
+            return base.OnExiting(next);
+        }
+    }
+}
diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs
new file mode 100644
index 0000000000..d1c2f6d6ee
--- /dev/null
+++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs
@@ -0,0 +1,57 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Screens;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Screens;
+
+namespace osu.Game.Overlays.Settings.Sections.Maintenance
+{
+    public class MigrationSelectScreen : OsuScreen
+    {
+        private DirectorySelector directorySelector;
+
+        [BackgroundDependencyLoader]
+        private void load()
+        {
+            InternalChild = new GridContainer
+            {
+                RelativeSizeAxes = Axes.Both,
+                RowDimensions = new[]
+                {
+                    new Dimension(GridSizeMode.Relative, 0.8f),
+                    new Dimension(),
+                },
+                Content = new[]
+                {
+                    new Drawable[] { directorySelector = new DirectorySelector { RelativeSizeAxes = Axes.Both } },
+                    new Drawable[]
+                    {
+                        new OsuButton
+                        {
+                            Anchor = Anchor.Centre,
+                            Origin = Anchor.Centre,
+                            Width = 300,
+                            Text = "Start",
+                            Action = start
+                        },
+                    }
+                }
+            };
+        }
+
+        private void start()
+        {
+            var target = directorySelector.CurrentDirectory.Value;
+            if (target.GetDirectories().Length > 0 || target.GetFiles().Length > 0)
+                target = target.CreateSubdirectory("osu-lazer");
+
+            ValidForResume = false;
+            this.Push(new MigrationRunScreen(target));
+        }
+    }
+}