diff --git a/mac/Gajim.icns b/mac/Gajim.icns
new file mode 100644
index 0000000000000000000000000000000000000000..ed943f18adebd4b5bb6a6af6562692d48c29322a
Binary files /dev/null and b/mac/Gajim.icns differ
diff --git a/mac/Info.plist.template b/mac/Info.plist.template
new file mode 100644
index 0000000000000000000000000000000000000000..5185e67dad36c82e8d0d2655600a3e246088c9b8
--- /dev/null
+++ b/mac/Info.plist.template
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>CFBundleExecutable</key>
+    <string>launch.sh</string>
+    <key>CFBundleIdentifier</key>
+    <string>org.gajim.mac</string>
+    <key>CFBundleInfoDictionaryVersion</key>
+    <string>6.0</string>
+    <key>CFBundleName</key>
+    <string>Gajim</string>
+    <key>CFBundlePackageType</key>
+    <string>APPL</string>
+    <key>CFBundleShortVersionString</key>
+    <string>${short_version_string}</string>
+    <key>CFBundleSignature</key>
+    <string>DASH</string>
+    <key>CFBundleVersion</key>
+    <string>${version}</string>
+    <key>CFBundleIconFile</key>
+    <string>Gajim</string>
+    <key>CFBundleURLTypes</key>
+    <array>
+        <dict>
+            <key>CFBundleTypeRole</key>
+            <string>Viewer</string>
+            <key>CFBundleURLName</key>
+            <string>XMPP</string>
+            <key>CFBundleURLSchemes</key>
+            <array>
+                <string>xmpp</string>
+            </array>
+        </dict>
+    </array>
+</dict>
+</plist>
diff --git a/mac/launch.sh.template b/mac/launch.sh.template
new file mode 100644
index 0000000000000000000000000000000000000000..a31ebf58002e4a27efcfa73bdf158aecf0431a82
--- /dev/null
+++ b/mac/launch.sh.template
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+${bin_path}
diff --git a/mac/makebundle.py b/mac/makebundle.py
new file mode 100755
index 0000000000000000000000000000000000000000..d9b8571b8f26a6f43d1beb51d5d6b533f4be1cb1
--- /dev/null
+++ b/mac/makebundle.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+import os
+import shutil
+import sys
+from string import Template
+from os.path import join
+import argparse
+
+EXEC_TEMPLATE = 'mac/launch.sh.template'
+PLIST_TEMPLATE = 'mac/Info.plist.template'
+ICNS_FILE = 'mac/Gajim.icns'
+
+
+def fill_template(in_path, out_path, vars):
+    with open(in_path, 'r') as f:
+        templ = Template(f.read())
+    filled_templ = templ.substitute(vars)
+    with open(out_path, 'w') as f:
+        f.write(filled_templ)
+
+
+def create_executable(exec_path, bin_path):
+    fill_template(EXEC_TEMPLATE, exec_path, {
+        'bin_path': bin_path
+    })
+    os.chmod(exec_path, 0o755)
+
+
+def create_plist(plist_path, version):
+    fill_template(PLIST_TEMPLATE, plist_path, {
+        'version': version,
+        'short_version_string': version
+    })
+
+
+if __name__ == '__main__':
+    if not os.path.isdir('mac'):
+        sys.exit("can't find the 'mac' directory. make sure you run "
+                 "this script from the project root")
+
+    parser = argparse.ArgumentParser(description='Create a macOS .app bundle.')
+    parser.add_argument('bundle', help='bundle output location')
+    parser.add_argument('--version', default='0.0.1',
+                        help='version number of the .app bundle')
+    parser.add_argument('--bin-path', default='/usr/local/bin/gajim',
+                        help='location of the actual executable')
+    args = parser.parse_args()
+
+    bundle = args.bundle
+
+    os.mkdir(bundle)
+    os.mkdir(join(bundle, 'Contents'))
+    os.mkdir(join(bundle, 'Contents/MacOS'))
+    os.mkdir(join(bundle, 'Contents/Resources'))
+
+    create_executable(join(bundle, 'Contents/MacOS/launch.sh'), bin_path=args.bin_path)
+    create_plist(join(bundle, 'Contents/Info.plist'), version=args.version)
+    shutil.copy(ICNS_FILE, join(bundle, 'Contents/Resources/Gajim.icns'))
diff --git a/mac/makeicns.py b/mac/makeicns.py
new file mode 100755
index 0000000000000000000000000000000000000000..b641a8773615cb1e042f3b374ef23a9258d5f763
--- /dev/null
+++ b/mac/makeicns.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+import os
+import shutil
+from argparse import ArgumentParser
+from subprocess import run
+
+ICON_SVG = 'gajim/data/icons/hicolor/scalable/apps/org.gajim.Gajim.svg'
+
+
+def create_icns(icon_path):
+    tmpdir = 'Gajim.iconset'
+    if os.path.isdir(tmpdir):
+        shutil.rmtree(tmpdir)
+    os.mkdir(tmpdir)
+
+    for size_pt in [16, 32, 128, 256, 512]:
+        for scale in [1, 2]:
+            size_px = scale * size_pt
+            scale_txt = '@{}'.format(scale) if scale != 1 else ''
+            png_fn = 'icon_{}x{}{}.png'.format(size_pt, size_pt, scale_txt)
+            png_path = os.path.join(tmpdir, png_fn)
+            run(['inkscape', '-z', '-e', png_path,
+                 '-w', str(size_px), '-h', str(size_px), '-y', '0',
+                 ICON_SVG])
+    run(['iconutil', '-c', 'icns', '-o', icon_path, tmpdir])
+
+    shutil.rmtree(tmpdir)
+
+
+if __name__ == '__main__':
+    parser = ArgumentParser(description='Create a macOS .icns icon. '
+                            'Requires Inkscape and iconutil (macOS).')
+    parser.add_argument('output', help='bundle output location')
+    args = parser.parse_args()
+
+    create_icns(args.output)