diff --git a/helpers/command.py b/helpers/command.py
index b452f6d..26abf09 100644
--- a/helpers/command.py
+++ b/helpers/command.py
@@ -71,6 +71,8 @@ class Command:
 
                 CLI.run_command(frontend_command, config.get("kobodocker_path"))
 
+            # ToDo When Python3 container is released.
+            # ToDo Move this to `kobocat` build as `kpi` doesn't use it anymore.
             pull_base_command = ["docker",
                                  "pull",
                                  "kobotoolbox/koboform_base"]
@@ -492,7 +494,7 @@ class Command:
         CLI.colored_print("KoBoToolbox has been updated", CLI.COLOR_SUCCESS)
 
         # update itself
-        git_command = ["git", "pull", "origin", "master"]
+        git_command = ["git", "pull", "origin", "two-databases-py3"]
         CLI.run_command(git_command)
         CLI.colored_print("KoBoInstall has been updated", CLI.COLOR_SUCCESS)
 
diff --git a/helpers/config.py b/helpers/config.py
index 19d63e9..d8a3db1 100644
--- a/helpers/config.py
+++ b/helpers/config.py
@@ -518,7 +518,8 @@ class Config:
             "redis_main_port": "6379",
             "redis_cache_port": "6380",
             "local_installation": Config.FALSE,
-            "block_common_http_ports": Config.TRUE
+            "block_common_http_ports": Config.TRUE,
+            "npm_container": Config.TRUE,
         }
 
     def __questions_advanced_options(self):
@@ -750,6 +751,13 @@ class Config:
                     CLI.colored_print("\t2) False")
                     self.__config["debug"] = CLI.get_response([Config.TRUE, Config.FALSE],
                                                               self.__config.get("debug", Config.TRUE))
+
+                    # Frontend development
+                    CLI.colored_print("How do you want to run `npm`?", CLI.COLOR_SUCCESS)
+                    CLI.colored_print("\t1) From within the container")
+                    CLI.colored_print("\t2) Locally")
+                    self.__config["npm_container"] = CLI.get_response([Config.TRUE, Config.FALSE],
+                                                                      self.__config.get("npm_container", Config.TRUE))
             else:
                 # Force reset paths
                 self.__reset_dev_mode(self.staging_mode)
diff --git a/helpers/setup.py b/helpers/setup.py
index acc7721..2353474 100644
--- a/helpers/setup.py
+++ b/helpers/setup.py
@@ -11,7 +11,7 @@ from helpers.config import Config
 
 class Setup:
 
-    KOBO_DOCKER_BRANCH = 'kobo-install-two-databases'
+    KOBO_DOCKER_BRANCH = 'kobo-install-two-databases-py3'
 
     @classmethod
     def clone_kobodocker(cls, config_object):
diff --git a/helpers/template.py b/helpers/template.py
index f5c267b..dfe05b8 100644
--- a/helpers/template.py
+++ b/helpers/template.py
@@ -164,8 +164,8 @@ class Template:
             "MAX_REQUESTS": config.get("max_requests", "512"),
             "SOFT_LIMIT": int(config.get("soft_limit", "128")) * 1024 * 1024,
             "POSTGRES_REPLICATION_PASSWORD": config.get("postgres_replication_password"),
-            "WSGI_SERVER": "runserver_plus" if config.get("dev_mode") == Config.TRUE else "uWSGI",
-            "USE_X_FORWARDED_HOST": "" if config.get("dev_mode") == Config.TRUE else "#",
+            "WSGI_SERVER": "runserver_plus" if config_object.dev_mode else "uWSGI",
+            "USE_X_FORWARDED_HOST": "" if config_object.dev_mode else "#",
             "OVERRIDE_POSTGRES_SETTINGS": "" if config.get("postgres_settings") == Config.TRUE else "#",
             "POSTGRES_APP_PROFILE": config.get("postgres_profile", ""),
             "POSTGRES_RAM": config.get("postgres_ram", ""),
@@ -201,6 +201,7 @@ class Template:
             "MAINTENANCE_DATE_ISO": config.get("maintenance_date_iso", ""),
             "MAINTENANCE_DATE_STR": config.get("maintenance_date_str", ""),
             "MAINTENANCE_EMAIL": config.get("maintenance_email", ""),
+            "USE_NPM_FROM_HOST": "" if config_object.dev_mode and config.get("npm_container") == Config.FALSE else "#",
         }
 
     @staticmethod
diff --git a/templates/kobo-docker/docker-compose.frontend.override.yml.tpl b/templates/kobo-docker/docker-compose.frontend.override.yml.tpl
index 0f6b2b4..5c2520a 100644
--- a/templates/kobo-docker/docker-compose.frontend.override.yml.tpl
+++ b/templates/kobo-docker/docker-compose.frontend.override.yml.tpl
@@ -35,6 +35,7 @@ services:
       - KPI_UWSGI_MAX_REQUESTS=${MAX_REQUESTS}
       - KPI_UWSGI_CHEAPER_RSS_LIMIT_SOFT=${SOFT_LIMIT}
       ${USE_HTTPS}- SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO, https
+      ${USE_NPM_FROM_HOST}- FRONTEND_DEV_MODE=host
     ${USE_DNS}extra_hosts:
       ${USE_PUBLIC_DNS}- ${KOBOFORM_SUBDOMAIN}.${PUBLIC_DOMAIN_NAME}:${LOCAL_INTERFACE_IP}
       ${USE_PUBLIC_DNS}- ${KOBOCAT_SUBDOMAIN}.${PUBLIC_DOMAIN_NAME}:${LOCAL_INTERFACE_IP}

Can't build frontend from local machine with npm run watch
## Description

On MacOS after fresh `kobo-install` run, calling `npm run watch` command from local machine will throw error `Local package.json exists, but node_modules missing, did you mean to install?`.

Guess: this happens because `node_modules` and `jsapp/fonts` directories are symlinked.

Note that it is possible to run `npm run watch` from within the docker container, but as my short experiments showed, it will be much slower than running the same command locally.

## Temp hack fix

After fresh install, I run these commands to fix the problem:

```
rm -rf node_modules && rm -rf jsapp/fonts && npm i && npm run copy-fonts
```

## Additional details

Related flowdock threads:

- https://www.flowdock.com/app/kobotoolbox/kobo/threads/VkhUiAXSbuixlKAm1-H9emE6yHU
- https://www.flowdock.com/app/kobotoolbox/kobo/threads/tJT-6guE_Qwp7JkSn3NreoL_vLL
diff --git a/tests/test_config.py b/tests/test_config.py
index b0db23a..be117d6 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -69,7 +69,7 @@ def test_installation():
 
 
 @patch("helpers.config.Config._Config__clone_repo", MagicMock(return_value=True))
-def test_dev_mode():
+def test_staging_mode():
     config_object = test_read_config()
     kc_repo_path = tempfile.mkdtemp()
     kpi_repo_path = tempfile.mkdtemp()
@@ -85,6 +85,9 @@ def test_dev_mode():
     shutil.rmtree(kc_repo_path)
     shutil.rmtree(kpi_repo_path)
 
+
+@patch("helpers.config.Config._Config__clone_repo", MagicMock(return_value=True))
+def test_dev_mode():
     config_object = test_installation()
 
     kc_repo_path = tempfile.mkdtemp()
@@ -92,13 +95,16 @@ def test_dev_mode():
 
     with patch("helpers.cli.CLI.colored_input") as mock_colored_input:
 
-        mock_colored_input.side_effect = iter(["8080", Config.TRUE, kc_repo_path, kpi_repo_path, Config.FALSE])
+        mock_colored_input.side_effect = iter(["8080", Config.TRUE, kc_repo_path, kpi_repo_path,
+                                               Config.FALSE, Config.FALSE])
+
         config_object._Config__questions_dev_mode()
         config = config_object.get_config()
         assert config_object.dev_mode
         assert not config_object.staging_mode
         assert config_object.get_config().get("exposed_nginx_docker_port") == "8080"
         assert config.get("kpi_path") == kpi_repo_path and config.get("kc_path") == kc_repo_path
+        assert config.get("npm_container") == Config.FALSE
 
     shutil.rmtree(kc_repo_path)
     shutil.rmtree(kpi_repo_path)

pip install -e .
parse_log_pytest
pytest
pytest
apt-get update apt-get install -y python3-pip tox
3.7
requirements_py3_tests.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///croot/attrs_1668696182826/work
certifi @ file:///croot/certifi_1671487769961/work/certifi
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1648562407465/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
-e git+https://github.com/kobotoolbox/kobo-install.git@45906a1316b59ff3c56ec82a078ffa354101e86e#egg=KoBoInstall
packaging @ file:///croot/packaging_1671697413597/work
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pytest==7.1.2
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions @ file:///croot/typing_extensions_1669924550328/work
zipp @ file:///croot/zipp_1672387121353/work

name: kobo-install
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib-metadata=4.11.3=py37h06a4308_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - typing_extensions=4.4.0=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
prefix: /opt/conda/envs/kobo-install

tests/test_config.py::test_dev_mode
tests/test_config.py::test_read_config tests/test_config.py::test_advanced_options tests/test_config.py::test_installation tests/test_config.py::test_staging_mode tests/test_config.py::test_server_roles_questions tests/test_config.py::test_use_https tests/test_config.py::test_proxy_letsencrypt tests/test_config.py::test_proxy_no_letsencrypt_advanced tests/test_config.py::test_proxy_no_letsencrypt tests/test_config.py::test_proxy_no_letsencrypt_retains_custom_nginx_proxy_port tests/test_config.py::test_no_proxy_no_ssl tests/test_config.py::test_proxy_no_ssl_advanced tests/test_config.py::test_port_allowed tests/test_config.py::test_create_directory tests/test_config.py::test_maintenance
templates/kobo-docker/docker-compose.frontend.override.yml.tpl helpers/setup.py helpers/template.py helpers/config.py helpers/command.py
templates/kobo-docker/docker-compose.frontend.override.yml.tpl helpers/setup.py helpers/template.py helpers/config.py helpers/command.py