diff --git a/MeshLib/MeshSearch/NodeSearch.cpp b/MeshLib/MeshSearch/NodeSearch.cpp
index 4b185b47742d8e075113f330c498d796d7a334b8..5bffe4d8cbb36af174cbd2d81e9bebef2720278b 100644
--- a/MeshLib/MeshSearch/NodeSearch.cpp
+++ b/MeshLib/MeshSearch/NodeSearch.cpp
@@ -69,6 +69,63 @@ std::size_t NodeSearch::searchUnused()
     return del_node_idx.size();
 }
 
+std::size_t NodeSearch::searchBoundaryNodes()
+{
+    std::vector<std::size_t> vec_boundary_nodes;
+    if (_mesh.getDimension() == 1)
+    {
+        for (MeshLib::Node const* n : _mesh.getNodes())
+            if (n->getElements().size() == 1)
+                vec_boundary_nodes.push_back(n->getID());
+    }
+    else if (_mesh.getDimension() == 2)
+    {
+        for (MeshLib::Element const* elem : _mesh.getElements())
+        {
+            if (elem->getDimension() < _mesh.getDimension())
+                continue;
+            if (!elem->isBoundaryElement())
+                continue;
+
+            std::size_t const n_edges (elem->getNumberOfEdges());
+            for (std::size_t i=0; i<n_edges; ++i)
+            {
+                if (elem->getNeighbor(i) != nullptr)
+                    continue;
+                std::unique_ptr<MeshLib::Element const> edge(elem->getEdge(i));
+                for (unsigned j=0; j<edge->getNumberOfNodes(); j++)
+                    vec_boundary_nodes.push_back(edge->getNode(j)->getID());
+            }
+        }
+    }
+    else
+    {
+        for (MeshLib::Element const* elem : _mesh.getElements())
+        {
+            if (elem->getDimension() < _mesh.getDimension())
+                continue;
+            if (!elem->isBoundaryElement())
+                continue;
+
+            std::size_t const n_faces (elem->getNumberOfFaces());
+            for (std::size_t i=0; i<n_faces; ++i)
+            {
+                if (elem->getNeighbor(i) != nullptr)
+                    continue;
+                std::unique_ptr<MeshLib::Element const> face(elem->getFace(i));
+                for (unsigned j=0; j<face->getNumberOfNodes(); j++)
+                    vec_boundary_nodes.push_back(face->getNode(j)->getID());
+            }
+        }
+    }
+    std::sort(vec_boundary_nodes.begin(), vec_boundary_nodes.end());
+    vec_boundary_nodes.erase(std::unique(vec_boundary_nodes.begin(), vec_boundary_nodes.end()), vec_boundary_nodes.end());
+
+
+    this->updateUnion(vec_boundary_nodes);
+    return vec_boundary_nodes.size();
+}
+
 void NodeSearch::updateUnion(const std::vector<std::size_t> &vec)
 {
     std::vector<std::size_t> vec_temp(vec.size() + _marked_nodes.size());
diff --git a/MeshLib/MeshSearch/NodeSearch.h b/MeshLib/MeshSearch/NodeSearch.h
index dfc2d38c6553039b86d51c391807689aba09edee..1392b5fa99ab58103783f1d0dac30c52e4bb2a86 100644
--- a/MeshLib/MeshSearch/NodeSearch.h
+++ b/MeshLib/MeshSearch/NodeSearch.h
@@ -36,6 +36,9 @@ public:
     /// Marks all unused nodes
     std::size_t searchUnused();
 
+    /// Marks all boundary nodes
+    std::size_t searchBoundaryNodes();
+
 private:
     /// Updates the vector of marked items with values from vec.
     void updateUnion(const std::vector<std::size_t> &vec);
diff --git a/Tests/MeshLib/TestNodeSearch.cpp b/Tests/MeshLib/TestNodeSearch.cpp
index 631f393c6d36ff90f85e0cc642a30df37c2b7b9d..1efc228e7b39c08d57c236e992ada6dc4e012a30 100644
--- a/Tests/MeshLib/TestNodeSearch.cpp
+++ b/Tests/MeshLib/TestNodeSearch.cpp
@@ -15,6 +15,7 @@
 #include "MeshLib/Elements/Element.h"
 #include "MeshLib/MeshSearch/NodeSearch.h"
 #include "MeshLib/MeshGenerators/RasterToMesh.h"
+#include "MeshLib/MeshGenerators/MeshGenerator.h"
 #include "MeshLib/MeshEditing/DuplicateMeshComponents.h"
 
 #include "GeoLib/Raster.h"
@@ -45,4 +46,61 @@ TEST(NodeSearch, UnusedNodes)
 }
 
 
+TEST(NodeSearch, BoundaryNodes1D)
+{
+    std::unique_ptr<MeshLib::Mesh> mesh (MeshLib::MeshGenerator::generateLineMesh(5, 1.0));
+    MeshLib::NodeSearch ns(*mesh);
+    ns.searchBoundaryNodes();
+    std::vector<std::size_t> searched_nodes = ns.getSearchedNodeIDs();
+    ASSERT_EQ(2, searched_nodes.size());
+    ASSERT_EQ(0u, searched_nodes[0]);
+    ASSERT_EQ(5u, searched_nodes[1]);
+}
+
+TEST(NodeSearch, BoundaryNodes2D)
+{
+    std::unique_ptr<MeshLib::Mesh> mesh (MeshLib::MeshGenerator::generateRegularQuadMesh(5, 5, 1.0, 1.0));
+    MeshLib::NodeSearch ns(*mesh);
+    ns.searchBoundaryNodes();
+    std::vector<std::size_t> searched_nodes = ns.getSearchedNodeIDs();
+    ASSERT_EQ(20, searched_nodes.size());
+    for (auto nodeid : searched_nodes)
+    {
+        auto &node = *mesh->getNode(nodeid);
+        bool isOnBnd = false;
+        for (unsigned i=0; i<mesh->getDimension(); i++)
+        {
+            if (node[i]==0.0 || node[i]==5.0)
+            {
+                isOnBnd = true;
+                break;
+            }
+        }
 
+        ASSERT_TRUE(isOnBnd);
+    }
+}
+
+TEST(NodeSearch, BoundaryNodes3D)
+{
+    std::unique_ptr<MeshLib::Mesh> mesh (MeshLib::MeshGenerator::generateRegularHexMesh(5, 5, 5, 1.0, 1.0, 1.0));
+    MeshLib::NodeSearch ns(*mesh);
+    ns.searchBoundaryNodes();
+    std::vector<std::size_t> searched_nodes = ns.getSearchedNodeIDs();
+    ASSERT_EQ(152u, searched_nodes.size());
+    for (auto nodeid : searched_nodes)
+    {
+        auto &node = *mesh->getNode(nodeid);
+        bool isOnBnd = false;
+        for (unsigned i=0; i<mesh->getDimension(); i++)
+        {
+            if (node[i]==0.0 || node[i]==5.0)
+            {
+                isOnBnd = true;
+                break;
+            }
+        }
+
+        ASSERT_TRUE(isOnBnd);
+    }
+}