diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1b823a2c19a3eabf1719d24b74771549a44a232e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+dist
+build
diff --git a/README.md b/README.md
index dddb1640a3ffdbff6318fd38b18bfb9b7cf6327c..cd6ce4e011e1cde40a4c79c5c6d08889b23e1514 100644
--- a/README.md
+++ b/README.md
@@ -11,3 +11,12 @@ This cli requires python 3.6 or greater:
 # Usage
 
 ``funkwhale --help``
+
+# Build the binary
+
+You can build the binarie for you platform using the following commands:
+
+    pip install .[dev]
+    pyinstaller cli.spec
+
+This will output a binary in `./dist/funkwhale`.
diff --git a/cli.spec b/cli.spec
new file mode 100644
index 0000000000000000000000000000000000000000..3116b1c844458af93f54a78717d930d394d59264
--- /dev/null
+++ b/cli.spec
@@ -0,0 +1,35 @@
+# -*- mode: python -*-
+
+block_cipher = None
+
+
+a = Analysis(
+    ["funkwhale_cli/cli.py"],
+    pathex=["/home/eliotberriot/projects/funkwhale/cli"],
+    binaries=[],
+    datas=[],
+    hiddenimports=[],
+    hookspath=[],
+    runtime_hooks=[],
+    excludes=[],
+    win_no_prefer_redirects=False,
+    win_private_assemblies=False,
+    cipher=block_cipher,
+    noarchive=False,
+)
+pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
+exe = EXE(
+    pyz,
+    a.scripts,
+    a.binaries,
+    a.zipfiles,
+    a.datas,
+    [],
+    name="funkwhale",
+    debug=False,
+    bootloader_ignore_signals=False,
+    strip=False,
+    upx=True,
+    runtime_tmpdir=None,
+    console=True,
+)
diff --git a/funkwhale_cli/cli.py b/funkwhale_cli/cli.py
index 1948581568d09aa488eafbbb95926768ff1b0bc3..c3170d0130f80a7ea4d4fdcde6672aee5cd01059 100644
--- a/funkwhale_cli/cli.py
+++ b/funkwhale_cli/cli.py
@@ -7,6 +7,14 @@ import datetime
 import dotenv
 import functools
 import keyring
+
+# importing the backends explicitely is required for PyInstaller to work
+import keyring.backends.kwallet
+import keyring.backends.Windows
+import keyring.backends.OS_X
+import keyring.backends.SecretService
+import keyring.backends.chainer
+
 import logging
 import math
 import urllib.parse
@@ -16,11 +24,12 @@ import pathvalidate
 import pathlib
 import urllib.parse
 import tqdm
-from . import api
-from . import config
-from . import exceptions
-from . import logs
-from . import output
+
+from funkwhale_cli import api
+from funkwhale_cli import config
+from funkwhale_cli import exceptions
+from funkwhale_cli import logs
+from funkwhale_cli import output
 
 click_log.basic_config(logs.logger)
 
@@ -173,7 +182,12 @@ def set_server(ctx, url, token, use_auth=True):
 @TOKEN_DECORATOR
 @click_log.simple_verbosity_option(logs.logger, expose_value=True)
 @click.pass_context
-def cli(ctx, env_file, url, verbosity, token, quiet):
+def cli(ctx, env_file, url, verbosity, token, quiet, no_login):
+    # small hack to fix some weird issues with pyinstaller and keyring
+    # there seems to be a cache issue somewhere
+    del keyring.backend.get_all_keyring.__wrapped__.always_returns
+    keyring.core.init_backend()
+    # /end of hack
     ctx.ensure_object(dict)
     logs.logger.disabled = quiet
     set_server(ctx, url, token, use_auth=not no_login)
diff --git a/setup.cfg b/setup.cfg
index a32bac142ab34dc26ce0e5466b25d65a2c82b3be..37b52206a221db941a37498121cf842214dad928 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -44,7 +44,7 @@ dev =
     pytest
     pytest-mock
     pytest-env
-
+    pyinstaller
 
 
 [options.packages.find]