diff --git a/nsfarm/board/_board.py b/nsfarm/board/_board.py
index a8c974d6240ec1a585fa8e44c8e3b6594009bc1e..313789fb0730c5277e15e06304c61ac309976287 100644
--- a/nsfarm/board/_board.py
+++ b/nsfarm/board/_board.py
@@ -8,7 +8,7 @@ import serial
 import serial.tools.miniterm
 from pexpect import fdpexpect
 from .. import cli
-from ..lxd import Container
+from ..lxd import LXDConnection, Container, NetInterface
 
 MINITERM_DEFAULT_EXIT = '\x1d'  # Ctrl+]
 MINITERM_DEFAULT_MENU = '\x14'  # Ctrl+T
@@ -63,9 +63,10 @@ class Board(abc.ABC):
         self._pexpect.sendline("")
         return cli.Uboot(self._pexpect)
 
-    def bootup(self, device_wan, os_branch):
+    def bootup(self, lxd_connection: LXDConnection, device_wan: NetInterface, os_branch: str) -> cli.Shell:
         """Boot board using TFTP boot. This ensures that board is booted up and ready to accept commands.
 
+        lxd_connection: instance of nsfarm.lxd.LXDConnection
         device_wan: Wan device to board. This is instance of nsfarm.lxd.NetInterface.
         os_branch: Turris OS branch to download medkit from.
 
@@ -74,7 +75,7 @@ class Board(abc.ABC):
         # First get U-Boot prompt
         uboot = self.uboot()
         # Now load image from TFTP
-        with Container("boot", devices=[device_wan, ]) as cont:
+        with Container(lxd_connection, "boot", devices=[device_wan, ]) as cont:
             ccli = cli.Shell(cont.pexpect())
             ccli.run(f"prepare_turris_image {os_branch}")
             uboot.run('setenv ipaddr 192.168.1.142')
diff --git a/nsfarm/lxd/__init__.py b/nsfarm/lxd/__init__.py
index 3427d4b0da9a4e9ac6c5ea93eb9a9cb83fd34564..d6cef98ba9e950800390ba98db7713d0576786e5 100644
--- a/nsfarm/lxd/__init__.py
+++ b/nsfarm/lxd/__init__.py
@@ -1,2 +1,3 @@
+from .connection import LXDConnection
 from .container import Container
 from .device import NetInterface
diff --git a/nsfarm/lxd/_lxd.py b/nsfarm/lxd/_lxd.py
deleted file mode 100644
index 2b477deb92380731dbf8adca418bfde2dec31285..0000000000000000000000000000000000000000
--- a/nsfarm/lxd/_lxd.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""Internal global LXD handle.
-"""
-import logging
-import pylxd
-
-IMAGES_SOURCE = "https://images.linuxcontainers.org"
-
-ROOT_PROFILE = "nsfarm-root"
-INTERNET_PROFILE = "nsfarm-internet"
-REQUIRED_PROFILES = (ROOT_PROFILE, INTERNET_PROFILE)
-
-# Global LXD handles
-images = None
-local = None
-
-
-def _profile_device(profile, check_func):
-    return any(check_func(dev) for dev in profile.devices.values())
-
-
-def connect():
-    """Make sure that we are connected to LXD.
-    """
-    # Suppress logging of pylxd components
-    logging.getLogger('ws4py').setLevel(logging.ERROR)
-    logging.getLogger('urllib3').setLevel(logging.ERROR)
-    # Initialize LXD connection to linuximages.org
-    global images
-    if images is None:
-        images = pylxd.Client(IMAGES_SOURCE)
-    # Initialize LXD connection to local server
-    global local
-    if local is None:
-        local = pylxd.Client()
-        # Verify profiles
-        for name in REQUIRED_PROFILES:
-            if not local.profiles.exists(name):
-                # TODO better exception
-                raise Exception(f"Missing required LXD profile: {name}")
-        root = local.profiles.get(ROOT_PROFILE)
-        internet = local.profiles.get(INTERNET_PROFILE)
-        if not _profile_device(root, lambda dev: dev["type"] == "disk"):
-            # TODO better exception
-            raise Exception("nsfarm-root does not provide disk device")
-        if not _profile_device(internet, lambda dev: dev["type"] == "nic" and dev["name"] == "internet"):
-            # TODO better exception
-            raise Exception("nsfarm-internet does not provide appropriate nic")
diff --git a/nsfarm/lxd/connection.py b/nsfarm/lxd/connection.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f1a19bd89e7aaf6976a066a5671d08cd3307854
--- /dev/null
+++ b/nsfarm/lxd/connection.py
@@ -0,0 +1,21 @@
+"""LXD connection for NSFarm.
+"""
+import logging
+import pylxd
+
+
+class LXDConnection:
+    """This is generic connection handler for LXD handling both connections to local and images server.
+    """
+    IMAGES_SOURCE = "https://images.linuxcontainers.org"
+
+    ROOT_PROFILE = "nsfarm-root"
+    INTERNET_PROFILE = "nsfarm-internet"
+
+    def __init__(self):
+        # Suppress logging of pylxd components
+        logging.getLogger('ws4py').setLevel(logging.ERROR)
+        logging.getLogger('urllib3').setLevel(logging.ERROR)
+
+        self.local = pylxd.Client()
+        self.images = pylxd.Client(self.IMAGES_SOURCE)
diff --git a/nsfarm/lxd/container.py b/nsfarm/lxd/container.py
index 14e67e948302a0dee13db31f74740c632a398f52..2ebf4dc8075f0309c30cff226d53b506ea1a3318 100644
--- a/nsfarm/lxd/container.py
+++ b/nsfarm/lxd/container.py
@@ -2,6 +2,7 @@
 """
 import os
 import time
+import typing
 import itertools
 import pathlib
 import hashlib
@@ -9,7 +10,8 @@ import logging
 import pexpect
 import pylxd
 from .. import cli
-from . import _lxd
+from .connection import LXDConnection
+from .device import NetInterface
 
 IMAGE_INIT_PATH = "/nsfarm-init.sh"  # Where we deploy initialization script for image
 
@@ -23,7 +25,9 @@ class Container:
     """
     # TODO log syslog somehow
 
-    def __init__(self, img_name, devices=(), internet=True):
+    def __init__(self, lxd_connection: LXDConnection, img_name: str, devices: typing.List[NetInterface] = (),
+                 internet: bool = True):
+        self._lxd = lxd_connection
         self._name = img_name
         self._internet = internet
         self._devices = tuple(devices)
@@ -35,17 +39,15 @@ class Container:
             raise Exception(f"There seems to be no file describing image: {self._file_path}")
         if not self._dir_path.is_dir():
             self._dir_path = None
-        # Make sure that we are connected to LXD
-        _lxd.connect()
         # Get parent
         with open(self._file_path) as file:
             # This reads second line of file while initial hash removed
             parent = next(itertools.islice(file, 1, 2))[1:].strip()
         self._parent = None
         if parent.startswith("nsfarm:"):
-            self._parent = Container(parent[7:])
+            self._parent = Container(lxd_connection, parent[7:])
         elif parent.startswith("images:"):
-            self._parent = _lxd.images.images.get_by_alias(parent[7:])
+            self._parent = self._lxd.images.images.get_by_alias(parent[7:])
         else:
             raise Exception(f"The file has parent from unknown source: {parent}: {self.fpath}")
         # Calculate identity hash and generate image name
@@ -91,8 +93,8 @@ class Container:
         """
         if self._lxd_image:
             return
-        if _lxd.local.images.exists(self._image_alias, alias=True):
-            self._lxd_image = _lxd.local.images.get_by_alias(self._image_alias)
+        if self._lxd.local.images.exists(self._image_alias, alias=True):
+            self._lxd_image = self._lxd.local.images.get_by_alias(self._image_alias)
             return
         # We do not have appropriate image so prepare it
         logger.warning("Bootstrapping image: %s", self._image_alias)
@@ -106,11 +108,11 @@ class Container:
         else:
             # We have to pull it from images
             image_source["mode"] = "pull"
-            image_source["server"] = _lxd.IMAGES_SOURCE
+            image_source["server"] = self._lxd.IMAGES_SOURCE
             image_source["alias"] = self._parent.fingerprint
         container_name = f"nsfarm-bootstrap-{self._name}-{self._hash}"
         try:
-            container = _lxd.local.containers.create({
+            container = self._lxd.local.containers.create({
                 'name': container_name,
                 'profiles': ['nsfarm-root', 'nsfarm-internet'],
                 'source': image_source
@@ -121,7 +123,7 @@ class Container:
                 raise
             logger.warning("Other instance is already bootsrapping image probably. "
                             "Waiting for following container to go away: %s", container_name)
-            while _lxd.local.containers.exists(container_name):
+            while self._lxd.local.containers.exists(container_name):
                 time.sleep(1)
             self.prepare_image()  # possibly get created image or try again
             return
@@ -163,7 +165,7 @@ class Container:
         for device in self._devices:
             devices.update(device.acquire(self))
         # Create and start container
-        self._lxd_container = _lxd.local.containers.create({
+        self._lxd_container = self._lxd.local.containers.create({
             'name': self._container_name(),
             'ephemeral': True,
             'profiles': profiles,
@@ -180,9 +182,9 @@ class Container:
 
     def _container_name(self, prefix="nsfarm"):
         name = f"{prefix}-{self._name}-{os.getpid()}"
-        if _lxd.local.containers.exists(name):
+        if self._lxd.local.containers.exists(name):
             i = 1
-            while _lxd.local.containers.exists(f"{name}-{i}"):
+            while self._lxd.local.containers.exists(f"{name}-{i}"):
                 i += 1
             name = f"{name}-{i}"
         return name
diff --git a/nsfarm/lxd/utils.py b/nsfarm/lxd/utils.py
index 15aeb9f4edad8c2b42ade3ee00cd6fb39c10e029..8f770e7c321f2d482475b21a413b5d469a1b8d9e 100644
--- a/nsfarm/lxd/utils.py
+++ b/nsfarm/lxd/utils.py
@@ -4,8 +4,8 @@ import os
 import logging
 from datetime import datetime
 import dateutil.parser
-from . import container
-from . import _lxd
+from .connection import LXDConnection
+from .container import Container, IMGS_DIR
 
 logger = logging.getLogger(__package__)
 
@@ -18,11 +18,11 @@ def clean(delta, dry_run=False):
 
     Returns list of (to be) removed images.
     """
-    _lxd.connect()
+    connection = LXDConnection()
     since = datetime.today() - delta
 
     removed = list()
-    for img in _lxd.local.images.all():
+    for img in connection.local.images.all():
         if not any(alias.startswith("nsfarm/") for alias in img.aliases):
             continue
         last_used = dateutil.parser.parse(
@@ -42,7 +42,7 @@ def all_images():
 
     This collects all *.sh files in imgs directory in root of nsfarm project.
     """
-    return (imgf[:-3] for imgf in os.listdir(container.IMGS_DIR) if imgf.endswith(".sh"))
+    return (imgf[:-3] for imgf in os.listdir(IMGS_DIR) if imgf.endswith(".sh"))
 
 
 def bootstrap(imgs=None):
@@ -53,11 +53,8 @@ def bootstrap(imgs=None):
     Returns True if all images were bootstrapped correctly.
     """
     success = True
+    connection = LXDConnection()
     for img in all_images() if imgs is None else imgs:
         logger.info("Trying to bootstrap: %s", img)
-        try:
-            container.Container(img).prepare_image()
-        except Exception:
-            success = False
-            logger.exception("Bootstrap failed for: %s", img)
+        Container(connection, img).prepare_image()
     return success
diff --git a/selftests/test_lxd.py b/selftests/test_lxd.py
new file mode 100644
index 0000000000000000000000000000000000000000..48a473e7dedea681bf2733fc0f4d065495e4d143
--- /dev/null
+++ b/selftests/test_lxd.py
@@ -0,0 +1,31 @@
+import pytest
+from nsfarm import lxd
+
+
+@pytest.fixture(name="connection", scope="module")
+def fixture_connection():
+    return lxd.LXDConnection()
+
+
+@pytest.mark.parametrize("profile", [
+    lxd.LXDConnection.ROOT_PROFILE,
+    lxd.LXDConnection.INTERNET_PROFILE,
+])
+def test_profiles_exists(connection, profile):
+    """Check that all profiles we need are configured in LXD.
+    """
+    assert connection.local.profiles.exists(profile)
+
+
+def test_profile_root(connection):
+    """Minimal sanity check of root profile.
+    """
+    profile = connection.local.profiles.get(lxd.LXDConnection.ROOT_PROFILE)
+    assert any(dev for dev in profile.devices.values() if dev["type"] == "disk")
+
+
+def test_profile_internet(connection):
+    """Minimal sanity check of internet profile.
+    """
+    profile = connection.local.profiles.get(lxd.LXDConnection.INTERNET_PROFILE)
+    assert any(dev for dev in profile.devices.values() if dev["type"] == "nic" and dev["name"] == "internet")
diff --git a/tests/conftest.py b/tests/conftest.py
index 07c73eabafe134c5d9ebd79e1875c593251fb37e..550cbe41afcdaff92dc4e6526f7e966d6e130c2a 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -70,6 +70,14 @@ def fixture_board(request):
     return brd
 
 
+# TODO probably mark this with some LXD available/configured mark
+@pytest.fixture(scope="session", name="lxd")
+def fixture_lxd(request):
+    """Provides access to nsfarm.lxd.LXDConnection instance.
+    """
+    return nsfarm.lxd.LXDConnection()
+
+
 @pytest.fixture(scope="session", name="wan", params=[pytest.param(None, marks=pytest.mark.wan)])
 def fixture_wan(request):
     """Top level fixture used to share WAN interface handler.
@@ -88,12 +96,12 @@ def fixture_lan1(request):
 # Boot and setup fixtures ##############################################################################################
 
 @pytest.fixture(name="board_serial", scope="session")
-def fixture_board_serial(request, board, wan):
+def fixture_board_serial(request, lxd, board, wan):
     """Boot board to Shell.
     Provides instance of nsfarm.cli.Shell()
     """
     request.addfinalizer(lambda: board.reset(True))
-    return board.bootup(wan, request.config.target_branch)
+    return board.bootup(lxd, wan, request.config.target_branch)
 
 
 @pytest.fixture(name="board_root_password", scope="session")
@@ -133,10 +141,10 @@ def fixture_client_board(board_serial, board_root_password, lan1_client):
 # Common containers ####################################################################################################
 
 @pytest.fixture(name="lan1_client", scope="module")
-def fixture_lan1_client(lan1):
+def fixture_lan1_client(lxd, lan1):
     """Starts client container on LAN1 and provides it.
     """
-    with nsfarm.lxd.Container('client', devices=[lan1, ], internet=False) as container:
+    with nsfarm.lxd.Container(lxd, 'client', devices=[lan1, ], internet=False) as container:
         yield container
 
 
@@ -144,13 +152,13 @@ def fixture_lan1_client(lan1):
 # Standard configuration ###############################################################################################
 
 @pytest.fixture(name="basic_isp", scope="module")
-def fixture_basic_isp(board, client_board, wan):
+def fixture_basic_isp(lxd, board, client_board, wan):
     """Basic config we consider general. It provides you with configured WAN.
 
     Returns handle for ISP container on WAN interface.
     """
     # TODO what about other settings that are part of guide
-    with nsfarm.lxd.Container('isp-common', devices=[wan, ]) as container:
+    with nsfarm.lxd.Container(lxd, 'isp-common', devices=[wan, ]) as container:
         client_board.run("uci set network.wan.proto='static'")
         client_board.run("uci set network.wan.ipaddr='172.16.1.42'")
         client_board.run("uci set network.wan.netmask='255.240.0.0'")
diff --git a/tests/network/test_wan.py b/tests/network/test_wan.py
index ffe58fd80cc6a4c818a841c0c38634540ce3e5c3..a2ecb048549c19d0c36cf07b79aaf417cb6f3fe0 100644
--- a/tests/network/test_wan.py
+++ b/tests/network/test_wan.py
@@ -16,11 +16,11 @@ class TestStatic(common.InternetTests):
     """
 
     @pytest.fixture(scope="class", autouse=True)
-    def client(self, board, client_board, wan):
+    def client(self, lxd, board, client_board, wan):
         """Configure WAN to use static IP
         """
         print("We are in client fixture once")
-        with nsfarm.lxd.Container('isp-common', devices=[wan, ]):
+        with nsfarm.lxd.Container(lxd, 'isp-common', devices=[wan, ]):
             # TODO implement some utility class to set and revert uci configs on router
             client_board.run("uci set network.wan.proto='static'")
             client_board.run("uci set network.wan.ipaddr='172.16.1.42'")
@@ -45,10 +45,10 @@ class TestDHCP(common.InternetTests):
     """
 
     @pytest.fixture(scope="class", autouse=True)
-    def client(self, board, client_board, wan):
+    def client(self, lxd, board, client_board, wan):
         """Configure WAN to use DHCP
         """
-        with nsfarm.lxd.Container('isp-dhcp', devices=[wan, ]):
+        with nsfarm.lxd.Container(lxd, 'isp-dhcp', devices=[wan, ]):
             client_board.run("uci set network.wan.proto='dhcp'")
             client_board.run("uci commit network")
             client_board.run("/etc/init.d/network restart")