diff --git a/basicswap/base.py b/basicswap/base.py
index 09ecb4b..bab182b 100644
--- a/basicswap/base.py
+++ b/basicswap/base.py
@@ -5,17 +5,18 @@
 # Distributed under the MIT software license, see the accompanying
 # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 
-import os
-import time
-import shlex
-import socks
-import random
-import socket
-import urllib
 import logging
-import threading
-import traceback
+import os
+import random
+import shlex
+import socket
+import socks
 import subprocess
+import sys
+import threading
+import time
+import traceback
+import urllib
 
 from sockshandler import SocksiPyHandler
 
@@ -42,9 +43,9 @@ def getaddrinfo_tor(*args):
 
 
 class BaseApp(DBMethods):
-    def __init__(self, fp, data_dir, settings, chain, log_name="BasicSwap"):
+    def __init__(self, data_dir, settings, chain, log_name="BasicSwap"):
+        self.fp = None
         self.log_name = log_name
-        self.fp = fp
         self.fail_code = 0
         self.mock_time_offset = 0
 
@@ -71,24 +72,33 @@ class BaseApp(DBMethods):
         self.default_socket_timeout = socket.getdefaulttimeout()
         self.default_socket_getaddrinfo = socket.getaddrinfo
 
+    def __del__(self):
+        if self.fp:
+            self.fp.close()
+
     def stopRunning(self, with_code=0):
         self.fail_code = with_code
         with self.mxDB:
             self.chainstate_delay_event.set()
             self.delay_event.set()
 
+    def openLogFile(self):
+        self.fp = open(os.path.join(self.data_dir, "basicswap.log"), "a")
+
     def prepareLogging(self):
         logging.setLoggerClass(BSXLogger)
         self.log = logging.getLogger(self.log_name)
         self.log.propagate = False
 
+        self.openLogFile()
+
         # Remove any existing handlers
         self.log.handlers = []
 
         formatter = logging.Formatter(
             "%(asctime)s %(levelname)s : %(message)s", "%Y-%m-%d %H:%M:%S"
         )
-        stream_stdout = logging.StreamHandler()
+        stream_stdout = logging.StreamHandler(sys.stdout)
         if self.log_name != "BasicSwap":
             stream_stdout.setFormatter(
                 logging.Formatter(
@@ -98,6 +108,7 @@ class BaseApp(DBMethods):
             )
         else:
             stream_stdout.setFormatter(formatter)
+        self.log_formatter = formatter
         stream_fp = logging.StreamHandler(self.fp)
         stream_fp.setFormatter(formatter)
 
diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py
index f1d056a..ec8e8b4 100644
--- a/basicswap/basicswap.py
+++ b/basicswap/basicswap.py
@@ -11,6 +11,7 @@ import concurrent.futures
 import copy
 import datetime as dt
 import json
+import logging
 import os
 import random
 import secrets
@@ -281,14 +282,13 @@ class BasicSwap(BaseApp):
 
     def __init__(
         self,
-        fp,
         data_dir,
         settings,
         chain,
         log_name="BasicSwap",
         transient_instance=False,
     ):
-        super().__init__(fp, data_dir, settings, chain, log_name)
+        super().__init__(data_dir, settings, chain, log_name)
 
         v = __version__.split(".")
         self._version = struct.pack(">HHH", int(v[0]), int(v[1]), int(v[2]))
@@ -352,6 +352,13 @@ class BasicSwap(BaseApp):
         self._expire_db_records_after = self.get_int_setting(
             "expire_db_records_after", 7 * 86400, 0, 31 * 86400
         )  # Seconds
+        self._max_logfile_bytes = self.settings.get(
+            "max_logfile_size", 100
+        )  # In MB 0 to disable truncation
+        if self._max_logfile_bytes > 0:
+            self._max_logfile_bytes *= 1024 * 1024
+        self._max_logfiles = self.get_int_setting("max_logfiles", 10, 1, 100)
+
         self._notifications_cache = {}
         self._is_encrypted = None
         self._is_locked = None
@@ -9724,6 +9731,51 @@ class BasicSwap(BaseApp):
                 self.checkAcceptedBids()
                 self._last_checked_expired = now
 
+                if self._max_logfile_bytes > 0:
+                    logfile_size: int = self.fp.tell()
+                    self.log.debug(f"Log file bytes: {logfile_size}.")
+                    if logfile_size > self._max_logfile_bytes:
+                        for i, log_handler in enumerate(self.log.handlers):
+                            stream_name = getattr(log_handler.stream, "name", "")
+                            if stream_name.endswith(".log"):
+                                del self.log.handlers[i]
+                                break
+
+                        self.fp.close()
+                        log_path = os.path.join(self.data_dir, "basicswap.log")
+                        if self._max_logfiles == 1:
+                            os.remove(log_path)
+                        else:
+                            last_log = os.path.join(
+                                self.data_dir,
+                                f"basicswap_{self._max_logfiles - 1:0>2}.log",
+                            )
+                            if os.path.exists(last_log):
+                                os.remove(last_log)
+
+                            for i in range(self._max_logfiles - 2, 0, -1):
+                                path_from = os.path.join(
+                                    self.data_dir, f"basicswap_{i:0>2}.log"
+                                )
+                                path_to = os.path.join(
+                                    self.data_dir, f"basicswap_{i + 1:0>2}.log"
+                                )
+                                if os.path.exists(path_from):
+                                    os.rename(path_from, path_to)
+
+                            log_path = os.path.join(self.data_dir, "basicswap.log")
+                            os.rename(
+                                log_path,
+                                os.path.join(self.data_dir, "basicswap_01.log"),
+                            )
+
+                        self.openLogFile()
+
+                        stream_fp = logging.StreamHandler(self.fp)
+                        stream_fp.setFormatter(self.log_formatter)
+                        self.log.addHandler(stream_fp)
+                        self.log.info("Log file rotated.")
+
             if now - self._last_checked_actions >= self.check_actions_seconds:
                 self.checkQueuedActions()
                 self._last_checked_actions = now
diff --git a/basicswap/bin/prepare.py b/basicswap/bin/prepare.py
index 076feb7..3443590 100755
--- a/basicswap/bin/prepare.py
+++ b/basicswap/bin/prepare.py
@@ -1733,48 +1733,45 @@ def test_particl_encryption(data_dir, settings, chain, use_tor_proxy):
     swap_client = None
     daemons = []
     daemon_args = ["-noconnect", "-nodnsseed", "-nofindpeers", "-nostaking"]
-    with open(os.path.join(data_dir, "basicswap.log"), "a") as fp:
-        try:
-            swap_client = BasicSwap(
-                fp, data_dir, settings, chain, transient_instance=True
-            )
-            if not swap_client.use_tor_proxy:
-                # Cannot set -bind or -whitebind together with -listen=0
-                daemon_args.append("-nolisten")
-            c = Coins.PART
-            coin_name = "particl"
-            coin_settings = settings["chainclients"][coin_name]
-            daemon_args += getCoreBinArgs(c, coin_settings, prepare=True)
-            extra_config = {"stdout_to_file": True}
-            if coin_settings["manage_daemon"]:
-                filename: str = getCoreBinName(c, coin_settings, coin_name + "d")
-                daemons.append(
-                    startDaemon(
-                        coin_settings["datadir"],
-                        coin_settings["bindir"],
-                        filename,
-                        daemon_args,
-                        extra_config=extra_config,
-                    )
+    try:
+        swap_client = BasicSwap(data_dir, settings, chain, transient_instance=True)
+        if not swap_client.use_tor_proxy:
+            # Cannot set -bind or -whitebind together with -listen=0
+            daemon_args.append("-nolisten")
+        c = Coins.PART
+        coin_name = "particl"
+        coin_settings = settings["chainclients"][coin_name]
+        daemon_args += getCoreBinArgs(c, coin_settings, prepare=True)
+        extra_config = {"stdout_to_file": True}
+        if coin_settings["manage_daemon"]:
+            filename: str = getCoreBinName(c, coin_settings, coin_name + "d")
+            daemons.append(
+                startDaemon(
+                    coin_settings["datadir"],
+                    coin_settings["bindir"],
+                    filename,
+                    daemon_args,
+                    extra_config=extra_config,
                 )
-                swap_client.setDaemonPID(c, daemons[-1].handle.pid)
-            swap_client.setCoinRunParams(c)
-            swap_client.createCoinInterface(c)
-            swap_client.waitForDaemonRPC(c, with_wallet=True)
+            )
+            swap_client.setDaemonPID(c, daemons[-1].handle.pid)
+        swap_client.setCoinRunParams(c)
+        swap_client.createCoinInterface(c)
+        swap_client.waitForDaemonRPC(c, with_wallet=True)
 
-            if swap_client.ci(c).isWalletEncrypted():
-                logger.info("Particl Wallet is encrypted")
-                if WALLET_ENCRYPTION_PWD == "":
-                    raise ValueError(
-                        "Must set WALLET_ENCRYPTION_PWD to add coin when Particl wallet is encrypted"
-                    )
-                swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
-        finally:
-            if swap_client:
-                swap_client.finalise()
-                del swap_client
-            for d in daemons:
-                finalise_daemon(d)
+        if swap_client.ci(c).isWalletEncrypted():
+            logger.info("Particl Wallet is encrypted")
+            if WALLET_ENCRYPTION_PWD == "":
+                raise ValueError(
+                    "Must set WALLET_ENCRYPTION_PWD to add coin when Particl wallet is encrypted"
+                )
+            swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
+    finally:
+        if swap_client:
+            swap_client.finalise()
+            del swap_client
+        for d in daemons:
+            finalise_daemon(d)
 
 
 def encrypt_wallet(swap_client, coin_type) -> None:
@@ -1793,217 +1790,206 @@ def initialise_wallets(
 
     coins_failed_to_initialise = []
 
-    with open(os.path.join(data_dir, "basicswap.log"), "a") as fp:
-        try:
-            swap_client = BasicSwap(
-                fp, data_dir, settings, chain, transient_instance=True
-            )
-            if not swap_client.use_tor_proxy:
-                # Cannot set -bind or -whitebind together with -listen=0
-                daemon_args.append("-nolisten")
-            coins_to_create_wallets_for = (
-                Coins.PART,
-                Coins.BTC,
-                Coins.LTC,
-                Coins.DOGE,
-                Coins.DCR,
-                Coins.DASH,
-            )
-            # Always start Particl, it must be running to initialise a wallet in addcoin mode
-            # Particl must be loaded first as subsequent coins are initialised from the Particl mnemonic
-            start_daemons = [
-                "particl",
-            ] + [c for c in with_coins if c != "particl"]
-            for coin_name in start_daemons:
-                coin_settings = settings["chainclients"][coin_name]
-                wallet_name = coin_settings.get("wallet_name", "wallet.dat")
-                c = swap_client.getCoinIdFromName(coin_name)
+    try:
+        swap_client = BasicSwap(data_dir, settings, chain, transient_instance=True)
+        if not swap_client.use_tor_proxy:
+            # Cannot set -bind or -whitebind together with -listen=0
+            daemon_args.append("-nolisten")
+        coins_to_create_wallets_for = (
+            Coins.PART,
+            Coins.BTC,
+            Coins.LTC,
+            Coins.DOGE,
+            Coins.DCR,
+            Coins.DASH,
+        )
+        # Always start Particl, it must be running to initialise a wallet in addcoin mode
+        # Particl must be loaded first as subsequent coins are initialised from the Particl mnemonic
+        start_daemons = [
+            "particl",
+        ] + [c for c in with_coins if c != "particl"]
+        for coin_name in start_daemons:
+            coin_settings = settings["chainclients"][coin_name]
+            wallet_name = coin_settings.get("wallet_name", "wallet.dat")
+            c = swap_client.getCoinIdFromName(coin_name)
 
-                if c == Coins.XMR:
-                    if coin_settings["manage_wallet_daemon"]:
-                        filename = (
-                            coin_name
-                            + "-wallet-rpc"
-                            + (".exe" if os.name == "nt" else "")
+            if c == Coins.XMR:
+                if coin_settings["manage_wallet_daemon"]:
+                    filename = (
+                        coin_name + "-wallet-rpc" + (".exe" if os.name == "nt" else "")
+                    )
+                    filename: str = getWalletBinName(
+                        c, coin_settings, coin_name + "-wallet-rpc"
+                    )
+                    daemons.append(
+                        startXmrWalletDaemon(
+                            coin_settings["datadir"],
+                            coin_settings["bindir"],
+                            filename,
                         )
-                        filename: str = getWalletBinName(
-                            c, coin_settings, coin_name + "-wallet-rpc"
+                    )
+            elif c == Coins.WOW:
+                if coin_settings["manage_wallet_daemon"]:
+                    filename: str = getWalletBinName(
+                        c, coin_settings, coin_name + "-wallet-rpc"
+                    )
+                    daemons.append(
+                        startXmrWalletDaemon(
+                            coin_settings["datadir"],
+                            coin_settings["bindir"],
+                            filename,
                         )
-                        daemons.append(
-                            startXmrWalletDaemon(
-                                coin_settings["datadir"],
-                                coin_settings["bindir"],
-                                filename,
+                    )
+            elif c == Coins.DCR:
+                pass
+            else:
+                if coin_settings["manage_daemon"]:
+                    filename: str = getCoreBinName(c, coin_settings, coin_name + "d")
+                    coin_args = (
+                        ["-nofindpeers", "-nostaking"] if c == Coins.PART else []
+                    )
+                    coin_args += getCoreBinArgs(c, coin_settings, prepare=True)
+
+                    if c == Coins.FIRO:
+                        coin_args += [
+                            "-hdseed={}".format(
+                                swap_client.getWalletKey(Coins.FIRO, 1).hex()
                             )
-                        )
-                elif c == Coins.WOW:
-                    if coin_settings["manage_wallet_daemon"]:
-                        filename: str = getWalletBinName(
-                            c, coin_settings, coin_name + "-wallet-rpc"
-                        )
-                        daemons.append(
-                            startXmrWalletDaemon(
-                                coin_settings["datadir"],
-                                coin_settings["bindir"],
-                                filename,
-                            )
-                        )
-                elif c == Coins.DCR:
-                    pass
-                else:
-                    if coin_settings["manage_daemon"]:
-                        filename: str = getCoreBinName(
-                            c, coin_settings, coin_name + "d"
-                        )
-                        coin_args = (
-                            ["-nofindpeers", "-nostaking"] if c == Coins.PART else []
-                        )
-                        coin_args += getCoreBinArgs(c, coin_settings, prepare=True)
-
-                        if c == Coins.FIRO:
-                            coin_args += [
-                                "-hdseed={}".format(
-                                    swap_client.getWalletKey(Coins.FIRO, 1).hex()
-                                )
-                            ]
-
-                        extra_config = {"stdout_to_file": True}
-                        daemons.append(
-                            startDaemon(
-                                coin_settings["datadir"],
-                                coin_settings["bindir"],
-                                filename,
-                                daemon_args + coin_args,
-                                extra_config=extra_config,
-                            )
-                        )
-                        swap_client.setDaemonPID(c, daemons[-1].handle.pid)
-                swap_client.setCoinRunParams(c)
-                swap_client.createCoinInterface(c)
-
-                if c in coins_to_create_wallets_for:
-                    if c == Coins.DCR:
-                        if coin_settings["manage_wallet_daemon"] is False:
-                            continue
-                        from basicswap.interface.dcr.util import createDCRWallet
-
-                        dcr_password = (
-                            coin_settings["wallet_pwd"]
-                            if WALLET_ENCRYPTION_PWD == ""
-                            else WALLET_ENCRYPTION_PWD
-                        )
-                        extra_opts = [
-                            '--appdata="{}"'.format(coin_settings["datadir"]),
-                            "--pass={}".format(dcr_password),
                         ]
 
-                        filename: str = getWalletBinName(c, coin_settings, "dcrwallet")
-                        args = [
-                            os.path.join(coin_settings["bindir"], filename),
-                            "--create",
-                        ] + extra_opts
-                        hex_seed = swap_client.getWalletKey(Coins.DCR, 1).hex()
-                        createDCRWallet(args, hex_seed, logger, threading.Event())
-                        continue
-                    swap_client.waitForDaemonRPC(c, with_wallet=False)
-                    # Create wallet if it doesn't exist yet
-                    wallets = swap_client.callcoinrpc(c, "listwallets")
-                    if wallet_name not in wallets:
-                        logger.info(
-                            f'Creating wallet "{wallet_name}" for {getCoinName(c)}.'
+                    extra_config = {"stdout_to_file": True}
+                    daemons.append(
+                        startDaemon(
+                            coin_settings["datadir"],
+                            coin_settings["bindir"],
+                            filename,
+                            daemon_args + coin_args,
+                            extra_config=extra_config,
                         )
+                    )
+                    swap_client.setDaemonPID(c, daemons[-1].handle.pid)
+            swap_client.setCoinRunParams(c)
+            swap_client.createCoinInterface(c)
 
-                        if c in (Coins.BTC, Coins.LTC, Coins.DOGE, Coins.DASH):
-                            # wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
+            if c in coins_to_create_wallets_for:
+                if c == Coins.DCR:
+                    if coin_settings["manage_wallet_daemon"] is False:
+                        continue
+                    from basicswap.interface.dcr.util import createDCRWallet
 
-                            use_descriptors = coin_settings.get(
-                                "use_descriptors", False
-                            )
+                    dcr_password = (
+                        coin_settings["wallet_pwd"]
+                        if WALLET_ENCRYPTION_PWD == ""
+                        else WALLET_ENCRYPTION_PWD
+                    )
+                    extra_opts = [
+                        '--appdata="{}"'.format(coin_settings["datadir"]),
+                        "--pass={}".format(dcr_password),
+                    ]
+
+                    filename: str = getWalletBinName(c, coin_settings, "dcrwallet")
+                    args = [
+                        os.path.join(coin_settings["bindir"], filename),
+                        "--create",
+                    ] + extra_opts
+                    hex_seed = swap_client.getWalletKey(Coins.DCR, 1).hex()
+                    createDCRWallet(args, hex_seed, logger, threading.Event())
+                    continue
+                swap_client.waitForDaemonRPC(c, with_wallet=False)
+                # Create wallet if it doesn't exist yet
+                wallets = swap_client.callcoinrpc(c, "listwallets")
+                if wallet_name not in wallets:
+                    logger.info(
+                        f'Creating wallet "{wallet_name}" for {getCoinName(c)}.'
+                    )
+
+                    if c in (Coins.BTC, Coins.LTC, Coins.DOGE, Coins.DASH):
+                        # wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
+
+                        use_descriptors = coin_settings.get("use_descriptors", False)
+                        swap_client.callcoinrpc(
+                            c,
+                            "createwallet",
+                            [
+                                wallet_name,
+                                False,
+                                True,
+                                WALLET_ENCRYPTION_PWD,
+                                False,
+                                use_descriptors,
+                            ],
+                        )
+                        if use_descriptors:
                             swap_client.callcoinrpc(
                                 c,
                                 "createwallet",
                                 [
-                                    wallet_name,
-                                    False,
+                                    coin_settings["watch_wallet_name"],
                                     True,
-                                    WALLET_ENCRYPTION_PWD,
+                                    True,
+                                    "",
                                     False,
                                     use_descriptors,
                                 ],
                             )
-                            if use_descriptors:
-                                swap_client.callcoinrpc(
-                                    c,
-                                    "createwallet",
-                                    [
-                                        coin_settings["watch_wallet_name"],
-                                        True,
-                                        True,
-                                        "",
-                                        False,
-                                        use_descriptors,
-                                    ],
-                                )
-                            swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
-                        else:
-                            swap_client.callcoinrpc(
-                                c,
-                                "createwallet",
-                                [
-                                    wallet_name,
-                                ],
-                            )
-                            if WALLET_ENCRYPTION_PWD != "":
-                                encrypt_wallet(swap_client, c)
-
-                        if c == Coins.LTC:
-                            password = (
-                                WALLET_ENCRYPTION_PWD
-                                if WALLET_ENCRYPTION_PWD != ""
-                                else None
-                            )
-                            swap_client.ci(Coins.LTC_MWEB).init_wallet(password)
-
-                if c == Coins.PART:
-                    if "particl" in with_coins:
-                        logger.info("Loading Particl mnemonic")
-                        if particl_wallet_mnemonic is None:
-                            particl_wallet_mnemonic = swap_client.callcoinrpc(
-                                Coins.PART, "mnemonic", ["new"]
-                            )["mnemonic"]
-                            generated_mnemonic = True
-                        swap_client.callcoinrpc(
-                            Coins.PART, "extkeyimportmaster", [particl_wallet_mnemonic]
-                        )
-                    # Particl wallet must be unlocked to call getWalletKey
-                    if WALLET_ENCRYPTION_PWD != "":
                         swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
-
-            for coin_name in with_coins:
-                c = swap_client.getCoinIdFromName(coin_name)
-                if c in (Coins.PART,):
-                    continue
-                if c not in (Coins.DCR,):
-                    # initialiseWallet only sets main_wallet_seedid_
-                    swap_client.waitForDaemonRPC(c)
-                try:
-                    swap_client.initialiseWallet(c, raise_errors=True)
-                except Exception as e:
-                    coins_failed_to_initialise.append((c, e))
-                if WALLET_ENCRYPTION_PWD != "" and c not in coins_to_create_wallets_for:
-                    try:
-                        swap_client.ci(c).changeWalletPassword(
-                            "", WALLET_ENCRYPTION_PWD
+                    else:
+                        swap_client.callcoinrpc(
+                            c,
+                            "createwallet",
+                            [
+                                wallet_name,
+                            ],
                         )
-                    except Exception as e:  # noqa: F841
-                        logger.warning(f"changeWalletPassword failed for {coin_name}.")
+                        if WALLET_ENCRYPTION_PWD != "":
+                            encrypt_wallet(swap_client, c)
 
-        finally:
-            if swap_client:
-                swap_client.finalise()
-                del swap_client
-            for d in daemons:
-                finalise_daemon(d)
+                    if c == Coins.LTC:
+                        password = (
+                            WALLET_ENCRYPTION_PWD
+                            if WALLET_ENCRYPTION_PWD != ""
+                            else None
+                        )
+                        swap_client.ci(Coins.LTC_MWEB).init_wallet(password)
+
+            if c == Coins.PART:
+                if "particl" in with_coins:
+                    logger.info("Loading Particl mnemonic")
+                    if particl_wallet_mnemonic is None:
+                        particl_wallet_mnemonic = swap_client.callcoinrpc(
+                            Coins.PART, "mnemonic", ["new"]
+                        )["mnemonic"]
+                        generated_mnemonic = True
+                    swap_client.callcoinrpc(
+                        Coins.PART, "extkeyimportmaster", [particl_wallet_mnemonic]
+                    )
+                # Particl wallet must be unlocked to call getWalletKey
+                if WALLET_ENCRYPTION_PWD != "":
+                    swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
+
+        for coin_name in with_coins:
+            c = swap_client.getCoinIdFromName(coin_name)
+            if c in (Coins.PART,):
+                continue
+            if c not in (Coins.DCR,):
+                # initialiseWallet only sets main_wallet_seedid_
+                swap_client.waitForDaemonRPC(c)
+            try:
+                swap_client.initialiseWallet(c, raise_errors=True)
+            except Exception as e:
+                coins_failed_to_initialise.append((c, e))
+            if WALLET_ENCRYPTION_PWD != "" and c not in coins_to_create_wallets_for:
+                try:
+                    swap_client.ci(c).changeWalletPassword("", WALLET_ENCRYPTION_PWD)
+                except Exception as e:  # noqa: F841
+                    logger.warning(f"changeWalletPassword failed for {coin_name}.")
+
+    finally:
+        if swap_client:
+            swap_client.finalise()
+            del swap_client
+        for d in daemons:
+            finalise_daemon(d)
 
     print("")
     for pair in coins_failed_to_initialise:
diff --git a/basicswap/bin/run.py b/basicswap/bin/run.py
index ddcfbea..091e857 100755
--- a/basicswap/bin/run.py
+++ b/basicswap/bin/run.py
@@ -272,8 +272,8 @@ def getCoreBinArgs(coin_id: int, coin_settings, prepare=False, use_tor_proxy=Fal
 
 
 def runClient(
-    fp, data_dir: str, chain: str, start_only_coins: bool, log_prefix: str = "BasicSwap"
-):
+    data_dir: str, chain: str, start_only_coins: bool, log_prefix: str = "BasicSwap"
+) -> int:
     global swap_client, logger
     daemons = []
     pids = []
@@ -298,7 +298,7 @@ def runClient(
     with open(settings_path) as fs:
         settings = json.load(fs)
 
-    swap_client = BasicSwap(fp, data_dir, settings, chain, log_name=log_prefix)
+    swap_client = BasicSwap(data_dir, settings, chain, log_name=log_prefix)
     logger = swap_client.log
 
     if os.path.exists(pids_path):
@@ -482,7 +482,6 @@ def runClient(
                     else cfg.DEFAULT_ALLOW_CORS
                 )
                 thread_http = HttpThread(
-                    fp,
                     settings["htmlhost"],
                     settings["htmlport"],
                     allow_cors,
@@ -548,6 +547,9 @@ def runClient(
         except Exception as e:
             swap_client.log.error(f"Error: {e}")
 
+    fail_code: int = swap_client.fail_code
+    del swap_client
+
     if os.path.exists(pids_path):
         with open(pids_path) as fd:
             lines = fd.read().split("\n")
@@ -561,6 +563,8 @@ def runClient(
         with open(pids_path, "w") as fd:
             fd.write(still_running)
 
+    return fail_code
+
 
 def printVersion():
     logger.info(
@@ -642,14 +646,11 @@ def main():
     if not os.path.exists(data_dir):
         os.makedirs(data_dir)
 
-    with open(os.path.join(data_dir, "basicswap.log"), "a") as fp:
-        logger.info(
-            os.path.basename(sys.argv[0]) + ", version: " + __version__ + "\n\n"
-        )
-        runClient(fp, data_dir, chain, start_only_coins, log_prefix)
+    logger.info(os.path.basename(sys.argv[0]) + ", version: " + __version__ + "\n\n")
+    fail_code = runClient(data_dir, chain, start_only_coins, log_prefix)
 
     print("Done.")
-    return swap_client.fail_code if swap_client is not None else 0
+    return fail_code
 
 
 if __name__ == "__main__":
diff --git a/basicswap/http_server.py b/basicswap/http_server.py
index 60df1f6..a994b4a 100644
--- a/basicswap/http_server.py
+++ b/basicswap/http_server.py
@@ -637,11 +637,10 @@ class HttpHandler(BaseHTTPRequestHandler):
 
 
 class HttpThread(threading.Thread, HTTPServer):
-    def __init__(self, fp, host_name, port_no, allow_cors, swap_client):
+    def __init__(self, host_name, port_no, allow_cors, swap_client):
         threading.Thread.__init__(self)
 
         self.stop_event = threading.Event()
-        self.fp = fp
         self.host_name = host_name
         self.port_no = port_no
         self.allow_cors = allow_cors
diff --git a/tests/basicswap/extended/test_dash.py b/tests/basicswap/extended/test_dash.py
index f04cd08..d77079f 100644
--- a/tests/basicswap/extended/test_dash.py
+++ b/tests/basicswap/extended/test_dash.py
@@ -408,9 +408,8 @@ class Test(unittest.TestCase):
             settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
             with open(settings_path) as fs:
                 settings = json.load(fs)
-            fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
             sc = BasicSwap(
-                fp, basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
+                basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
             )
             cls.swap_clients.append(sc)
             sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
@@ -423,7 +422,7 @@ class Test(unittest.TestCase):
 
             sc.start()
 
-            t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
+            t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
             cls.http_threads.append(t)
             t.start()
 
@@ -484,7 +483,6 @@ class Test(unittest.TestCase):
             t.join()
         for c in cls.swap_clients:
             c.finalise()
-            c.fp.close()
 
         stopDaemons(cls.daemons)
 
diff --git a/tests/basicswap/extended/test_network.py b/tests/basicswap/extended/test_network.py
index de6bd14..872ed2d 100644
--- a/tests/basicswap/extended/test_network.py
+++ b/tests/basicswap/extended/test_network.py
@@ -325,9 +325,7 @@ class Test(unittest.TestCase):
                 settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
                 with open(settings_path) as fs:
                     settings = json.load(fs)
-                fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
                 sc = BasicSwap(
-                    fp,
                     basicswap_dir,
                     settings,
                     "regtest",
@@ -338,7 +336,7 @@ class Test(unittest.TestCase):
                 sc.setDaemonPID(Coins.PART, cls.part_daemons[i].handle.pid)
                 sc.start()
 
-                t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
+                t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
                 cls.http_threads.append(t)
                 t.start()
 
@@ -397,7 +395,6 @@ class Test(unittest.TestCase):
             t.join()
         for c in cls.swap_clients:
             c.finalise()
-            c.fp.close()
 
         stopDaemons(cls.part_daemons)
         stopDaemons(cls.btc_daemons)
diff --git a/tests/basicswap/extended/test_nmc.py b/tests/basicswap/extended/test_nmc.py
index d521203..ba3e544 100644
--- a/tests/basicswap/extended/test_nmc.py
+++ b/tests/basicswap/extended/test_nmc.py
@@ -365,9 +365,8 @@ class Test(unittest.TestCase):
             settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
             with open(settings_path) as fs:
                 settings = json.load(fs)
-            fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
             sc = BasicSwap(
-                fp, basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
+                basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
             )
             cls.swap_clients.append(sc)
 
@@ -376,7 +375,7 @@ class Test(unittest.TestCase):
             sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
             sc.start()
 
-            t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
+            t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
             cls.http_threads.append(t)
             t.start()
 
@@ -437,7 +436,6 @@ class Test(unittest.TestCase):
             t.join()
         for c in cls.swap_clients:
             c.finalise()
-            c.fp.close()
 
         stopDaemons(cls.daemons)
         cls.http_threads.clear()
diff --git a/tests/basicswap/extended/test_pivx.py b/tests/basicswap/extended/test_pivx.py
index efbd1f4..88c037b 100644
--- a/tests/basicswap/extended/test_pivx.py
+++ b/tests/basicswap/extended/test_pivx.py
@@ -414,9 +414,8 @@ class Test(unittest.TestCase):
             settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
             with open(settings_path) as fs:
                 settings = json.load(fs)
-            fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
             sc = BasicSwap(
-                fp, basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
+                basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
             )
             cls.swap_clients.append(sc)
             sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
@@ -424,7 +423,7 @@ class Test(unittest.TestCase):
             sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
             sc.start()
 
-            t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
+            t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
             cls.http_threads.append(t)
             t.start()
 
@@ -482,7 +481,6 @@ class Test(unittest.TestCase):
             t.join()
         for c in cls.swap_clients:
             c.finalise()
-            c.fp.close()
 
         stopDaemons(cls.daemons)
         cls.http_threads.clear()
diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py
index 0b80682..a85c7db 100644
--- a/tests/basicswap/test_xmr.py
+++ b/tests/basicswap/test_xmr.py
@@ -654,9 +654,7 @@ class BaseTest(unittest.TestCase):
                     if cls.restore_instance and i == 1:
                         cls.network_key = settings["network_key"]
                         cls.network_pubkey = settings["network_pubkey"]
-                fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
                 sc = BasicSwap(
-                    fp,
                     basicswap_dir,
                     settings,
                     "regtest",
@@ -684,7 +682,7 @@ class BaseTest(unittest.TestCase):
                     # Import a random seed to keep the existing test behaviour. BTC core rescans even with timestamp: now.
                     sc.ci(Coins.BTC).initialiseWallet(random.randbytes(32))
 
-                t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
+                t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
                 cls.http_threads.append(t)
                 t.start()
             # Set future block rewards to nowhere (a random address), so wallet amounts stay constant
@@ -952,7 +950,6 @@ class BaseTest(unittest.TestCase):
         logging.info("Stopping swap clients")
         for c in cls.swap_clients:
             c.finalise()
-            c.fp.close()
 
         logging.info("Stopping coin nodes")
         stopDaemons(cls.xmr_daemons)