diff --git a/.ci/build_doc.sh b/.ci/build_doc.sh
index 7aa453b19b873e9761ea8ce04d9f07b34dbb6f42..e15a2d163c9edf257b430ffd0fd916304d669949 100644
--- a/.ci/build_doc.sh
+++ b/.ci/build_doc.sh
@@ -4,4 +4,5 @@ set -e
 
 pip install -r requirements-dev.txt
 python setup.py doc
-mv doc/html doc/"$CI_COMMIT_REF_NAME"
+mkdir -p public
+mv doc/html/* public/
diff --git a/.ci/deploy_doc.sh b/.ci/deploy_doc.sh
deleted file mode 100644
index a8fdc6559ff0612789e52e9b584c10bcf1a7e8da..0000000000000000000000000000000000000000
--- a/.ci/deploy_doc.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-rsync -rv doc/"$CI_COMMIT_REF_NAME" "docdeploy:"
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 50be73b05b0cbc9974a2bebf6eaab71ec941dcb2..bb7cb0c0cbd4a3f924b012d1f2cc645ea106195e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,12 +9,12 @@
 #
 #    2. style:  Check coding style
 #
-#    3. doc:    Building API documentation
+#    3. doc:    Building and upload API documentation using GitLab Pages.
 #
 #    4. build:  Building source and wheel distributions
 #
-#    5. deploy: Uploading the build outputs to pypi/hosting servers, and the
-#               documentation to a hosting server.
+#    5. deploy: Uploading the build outputs to pypi/hosting servers.
+#
 #
 # Custom docker images are used for several jobs - these images are
 # available at:
@@ -24,13 +24,10 @@
 # The test and style stages are executed on all branches of upstream and fork
 # repositories.
 #
-# The doc stage, and the deploy-doc job, is executed on all branches of the
-# upstream repository.
-#
-# The build stage, and the remaining jobs in the deploy stage, are only
-# executed on the upstream repository, and only for release tags.
+# The doc stage is executed on release branches of the upstream repository.
 #
-# The deploy stages are manually instantiated.
+# The build and deploy stages are executed on tags on the upstream
+# repository, and the deploy stage must be manually instantiated.
 #
 # Most of the logic for each job is defined in shell scripts in the .ci
 # sub-directory.
@@ -61,16 +58,10 @@ stages:
 #   - SSH_PRIVATE_KEY_FSL_DOWNLOAD   - private key for downloading some FSL
 #                                      files from a remote server (FSL_HOST)
 #
-#   - SSH_PRIVATE_KEY_DOC_DEPLOY     - private key for rsyncing documentation
-#                                      to remote host (DOC_HOST)
-#
 #   - SSH_SERVER_HOSTKEYS            - List of trusted SSH hosts
 #
-#   - DOC_HOST:                      - Username@host to upload documentation to
-#                                      (e.g. "paulmc@jalapeno.fmrib.ox.ac.uk")
-#
 #   - FSL_HOST:                      - Username@host to download FSL data from
-#                                      (e.g. "paulmc@jalapeno.fmrib.ox.ac.uk")
+#                                      (most likely "paulmc@localhost")
 #
 #   - FSL_ATLAS_DIR:                 - Location of the FSL atlas data on
 #                                      FSL_HOST.
@@ -110,6 +101,11 @@ variables:
     - master@fsl/fslpy
 
 
+.only_release_branches: &only_release_branches
+  only:
+    - /^v.+$/@fsl/fslpy
+
+
 .only_releases: &only_releases
   only:
     - tags@fsl/fslpy
@@ -201,12 +197,18 @@ style:
     TEST_STYLE:  "true"
 
 
-###########
-# Doc stage
-###########
+#############
+# Pages stage
+#############
+
+# I would like to have separate doc deploys for
+# both the master and latest release branches,
+# but this is awkward with gitlab pages. So
+# currently the most recently executed pages
+# job is the one that gets deployed.
 
-build-doc:
-  <<: *only_upstream
+pages:
+  <<: *only_release_branches
 
   tags:
    - docker
@@ -218,9 +220,8 @@ build-doc:
     - bash ./.ci/build_doc.sh
 
   artifacts:
-    expire_in: 1 day
     paths:
-      - doc/$CI_COMMIT_REF_NAME
+      - public
 
 
 #############
@@ -252,23 +253,6 @@ build-pypi-dist:
 ##############
 
 
-deploy-doc:
-  <<: *only_upstream
-  <<: *setup_ssh
-  stage: deploy
-  when:  manual
-  image: python:3.7
-
-  tags:
-    - docker
-
-  dependencies:
-    - build-doc
-
-  script:
-    - bash ./.ci/deploy_doc.sh
-
-
 deploy-pypi:
   <<: *only_releases
   <<: *setup_ssh
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index ae4a1b6fe971ff6aa14b7a7edfa57440d5b328a3..10c1c88da120f620f30ad21ea131693453c51544 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -10,7 +10,14 @@ order.
 Changed
 ^^^^^^^
 
-
+* The ``fslpy`` API ocumentation is now hosted at
+  https://open.win.ox.ac.uk/fsl/fslpy
+* The :mod:`fsl` and :mod:`fsl.scripts` packages have been changed from being
+  `pkgutil-style
+  <https://packaging.python.org/guides/packaging-namespace-packages/#pkgutil-style-namespace-packages>`_
+  namespace packages to now being `native
+  <https://packaging.python.org/guides/packaging-namespace-packages/#native-namespace-packages>`_
+  namespace packages.
 * The :class:`.TaskThread` now allows an error handler function to be
   specified, which is run on the :mod:`.idle` loop.
 * The :func:`.bids.loadMetadata` function no long resolves sym-links when
diff --git a/doc/fsl.rst b/doc/fsl.rst
deleted file mode 100644
index c92138a86ddb95c109a747fa3a4624d6d74613d5..0000000000000000000000000000000000000000
--- a/doc/fsl.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-``fsl``
-=======
-
-.. toctree::
-   :hidden:
-
-   fsl.data
-   fsl.scripts
-   fsl.utils
-   fsl.transform
-   fsl.version
-   fsl.wrappers
-
-.. automodule:: fsl
-    :members:
-    :undoc-members:
-    :show-inheritance:
diff --git a/doc/fsl.scripts.rst b/doc/fsl.scripts.rst
index 1f7a720be2ea967b69e6913108288b5bcc16763d..ccd0c69868b29b0f1103cc7c70d76cf3e13df81a 100644
--- a/doc/fsl.scripts.rst
+++ b/doc/fsl.scripts.rst
@@ -20,7 +20,11 @@
    fsl.scripts.Text2Vest
    fsl.scripts.Vest2Text
 
-.. automodule:: fsl.scripts
-    :members:
-    :undoc-members:
-    :show-inheritance:
+
+The ``fsl.scripts`` package contains all of the executable scripts provided by
+``fslpy``, and other python-based FSL packages.
+
+
+The ``fsl.scripts`` package is a `native namespace package
+<https://packaging.python.org/guides/packaging-namespace-packages/>`_, which
+means that it can be used by other Python libraries.
diff --git a/doc/index.rst b/doc/index.rst
index 2ff98d800e9959b210b874571db3dd7baef8c230..ba5f9899ae3b73750a4baf78cb0a5267e4e66662 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -8,24 +8,53 @@ within `FSL <https://fsl.fmrib.ox.ac.uk/fsl/fslwiki>`_ and by
 |fsleyes_apidoc|_.
 
 
-The top-level Python package for ``fslpy`` is called ``fsl``.  It is broadly
-split into the following sub-packages:
+The top-level Python package for ``fslpy`` is called :mod:`fsl`.  It is
+broadly split into the following sub-packages:
+
++----------------------+-----------------------------------------------------+
+| :mod:`fsl.data`      | contains data abstractions and I/O routines for a   |
+|                      | range of FSL and neuroimaging file types. Most I/O  |
+|                      | routines use `nibabel <https://nipy.org/nibabel/>`_ |
+|                      | extensively.                                        |
++----------------------+-----------------------------------------------------+
+| :mod:`fsl.utils`     | contains a range of miscellaneous utilities,        |
+|                      | including :mod:`fsl.utils.path`,                    |
+|                      | :mod:`fsl.utils.run`, and :mod:`fsl.utils.bids`     |
++----------------------+-----------------------------------------------------+
+| :mod:`fsl.scripts`   | contains a range of scripts which are installed as  |
+|                      | FSL commands.                                       |
++----------------------+-----------------------------------------------------+
+| :mod:`fsl.transform` | contains functions and classes for working with     |
+|                      | FSL-style linear and non-linear transformations.    |
++----------------------+-----------------------------------------------------+
+| :mod:`fsl.version`   | simply contains the ``fslpy`` version number.       |
++----------------------+-----------------------------------------------------+
+| :mod:`fsl.wrappers`  | contains Python functions which can be used to      |
+|                      | invoke FSL commands.                                |
++----------------------+-----------------------------------------------------+
+
+The :mod:`fsl` package provides the top-level Python package namespace for
+``fslpy``, and for other FSL python libaries. It is a `native namespace
+package <https://packaging.python.org/guides/packaging-namespace-packages/>`_,
+which means that there is no ``fsl/__init__.py`` file.
+
+
+Other libraries can use the ``fsl`` package namepace simply by also omitting a
+``fsl/__init__.py`` file, and by ensuring that there are no naming conflicts
+with any sub-packages of ``fslpy`` or any other projects which use the ``fsl``
+package namespace.
 
 
-.. autosummary::
-
-   fsl.data
-   fsl.utils
-   fsl.scripts
-   fsl.transform
-   fsl.version
-   fsl.wrappers
-
 
 .. toctree::
    :hidden:
 
    self
-   fsl
+   fsl.data
+   fsl.scripts
+   fsl.transform
+   fsl.utils
+   fsl.wrappers
+   fsl.version
    contributing
    changelog
diff --git a/fsl/__init__.py b/fsl/__init__.py
deleted file mode 100644
index 1a3917dc763ec2e28bb5f21fe00ebe71154590fe..0000000000000000000000000000000000000000
--- a/fsl/__init__.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python
-#
-# __init__.py - The fslpy library.
-#
-# Author: Paul McCarthy <pauldmccarthy@gmail.com>
-#
-"""The :mod:`fsl` package is a library which contains convenience classes
-and functions for use by FSL python tools. It is broadly split into the
-following sub-packages:
-
-.. autosummary::
-
-   fsl.data
-   fsl.utils
-   fsl.scripts
-   fsl.transform
-   fsl.version
-   fsl.wrappers
-
-.. note:: The ``fsl`` namespace is a ``pkgutil``-style *namespace package* -
-          it can be used across different projects - see
-          https://packaging.python.org/guides/packaging-namespace-packages/
-          for details.
-"""
-
-__path__ = __import__('pkgutil').extend_path(__path__, __name__)  # noqa
diff --git a/fsl/scripts/__init__.py b/fsl/scripts/__init__.py
deleted file mode 100644
index 499b221767c7e66e8ff8bac70c7cc1ff14cf5120..0000000000000000000000000000000000000000
--- a/fsl/scripts/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-#
-# __init__.py - The fsl.scripts package.
-#
-# Author: Paul McCarthy <pauldmccarthy@gmail.com>
-#
-"""The ``fsl.scripts`` package contains all of the executable scripts provided
-by ``fslpy``, and other python-based FSL packages.
-
-.. note:: The ``fsl.scripts`` namespace is a ``pkgutil``-style *namespace
-          package* - it can be used across different projects - see
-          https://packaging.python.org/guides/packaging-namespace-packages/ for
-          details.
-"""
-
-__path__ = __import__('pkgutil').extend_path(__path__, __name__)  # noqa
diff --git a/fsl/utils/bids.py b/fsl/utils/bids.py
index eb36a5dc803f06aa2d4a7bd23cde068513c92253..671fb4c28650d912ff1579ab350cf5e99ae0c116 100644
--- a/fsl/utils/bids.py
+++ b/fsl/utils/bids.py
@@ -18,7 +18,8 @@
 All of the other functions in this module should not be considered part of the
 public API.
 
-.. see:: https://bids-specification.readthedocs.io/en/stable/
+See https://bids-specification.readthedocs.io/en/stable/ for more information
+about BIDS.
 
 .. note::  The `pybids <https://bids-standard.github.io/pybids/>`_ library is
            a more suitable choice if you are after a more robust and featured
diff --git a/fsl/utils/filetree/__init__.py b/fsl/utils/filetree/__init__.py
index f037014ea390477bff20fb289d99d596802cff3e..174b59cc63f389cf896c482f22192254a2061a0c 100644
--- a/fsl/utils/filetree/__init__.py
+++ b/fsl/utils/filetree/__init__.py
@@ -325,6 +325,7 @@ from .query import FileTreeQuery
 import fsl.utils.deprecated as deprecated
 
 deprecated.warn('fsl.utils.filetree',
+                stacklevel=2,
                 vin='3.6.0',
                 rin='4.0.0',
                 msg='The filetree package is now released as a separate '
diff --git a/setup.py b/setup.py
index 1a971bf2a3eb5facc9b5b4ca56168ee8aa9b485d..ebfa3afea50ef5e16cb9ccdeda8022dac84d3876 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@ import os.path as op
 import            shutil
 
 from setuptools import setup
-from setuptools import find_packages
+from setuptools import find_namespace_packages
 from setuptools import Command
 
 
@@ -27,7 +27,7 @@ with open(op.join(basedir, 'requirements.txt'), 'rt') as f:
 with open(op.join(basedir, 'requirements-extra.txt'), 'rt') as f:
     extra_requires = {'extras' : [l.strip() for l in f.readlines()]}
 
-packages = find_packages(include=('fsl', 'fsl.*'))
+packages = find_namespace_packages(include=('fsl', 'fsl.*'))
 
 # Figure out the current fslpy version, as defined in fsl/version.py. We
 # don't want to import the fsl package,  as this may cause build problems.
diff --git a/tests/test_fslsub.py b/tests/test_fslsub.py
index 1268a67f588cd977265e468bf14f89b8e9950d71..026cb66d8a421fde1ef492bb7adb897e2fbf9ae2 100644
--- a/tests/test_fslsub.py
+++ b/tests/test_fslsub.py
@@ -15,7 +15,7 @@ import contextlib
 import argparse
 import pytest
 
-import fsl
+import fsl.version as fv
 from fsl.utils         import fslsub, run
 from fsl.utils.tempdir import tempdir
 
@@ -63,7 +63,7 @@ with open('{{}}.o{{}}'.format(cmd, jobid), 'w') as stdout, \
 
 print(str(jobid))
 sys.exit(0)
-""".format(op.dirname(fsl.__file__)).strip()
+""".format(op.dirname(op.join(fv.__file__, '..'))).strip()
 
 @contextlib.contextmanager
 def fslsub_mockFSLDIR():