diff --git a/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java b/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java
index 632d9d6fef2c5c228e1061791f701458a7e7392d..efa2e1adb49dcee0ed8b344a79e7f9f5c5610f0e 100644
--- a/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java
+++ b/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java
@@ -28,6 +28,7 @@ import java.util.stream.Collectors;
 import javax.sql.DataSource;
 import net.ivoa.xml.vospace.v2.ContainerNode;
 import net.ivoa.xml.vospace.v2.DataNode;
+import net.ivoa.xml.vospace.v2.LinkNode;
 import net.ivoa.xml.vospace.v2.Property;
 import net.ivoa.xml.vospace.v2.View;
 import org.slf4j.Logger;
@@ -69,8 +70,8 @@ public class NodeDAO {
         StringBuilder sb = new StringBuilder();
         sb.append("INSERT INTO node");
         sb.append(" (name, job_id, creator_id, group_read, group_write,");
-        sb.append(" is_public, parent_path, parent_relative_path, type, accept_views, provide_views)");
-        sb.append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+        sb.append(" is_public, parent_path, parent_relative_path, type, accept_views, provide_views, target)");
+        sb.append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
 
         jdbcTemplate.update(conn -> {
             PreparedStatement ps = conn.prepareStatement(sb.toString());
@@ -90,6 +91,11 @@ public class NodeDAO {
             ps.setObject(++i, NodeUtils.getDbNodeType(myNode), Types.OTHER);
             ps.setObject(++i, fromViewsToArray(ps, myNode, d -> d.getAccepts()), Types.OTHER);
             ps.setObject(++i, fromViewsToArray(ps, myNode, d -> d.getProvides()), Types.OTHER);
+            if(myNode instanceof LinkNode) {
+                ps.setString(++i, ((LinkNode) myNode).getTarget());
+            } else {
+                ps.setNull(++i, Types.VARCHAR);
+            }
             return ps;
         });
     }
@@ -98,7 +104,7 @@ public class NodeDAO {
 
         String sql = "SELECT (CASE WHEN c.path = n.path THEN ? ELSE (? || ? || c.name) END) AS vos_path, c.node_id, c.name,\n"
                 + "c.type, c.async_trans, c.sticky, c.job_id IS NOT NULL AS busy_state, c.creator_id, c.group_read, c.group_write,\n"
-                + "c.is_public, c.content_length, c.created_on, c.last_modified, c.accept_views, c.provide_views, c.quota, c.content_md5\n"
+                + "c.is_public, c.content_length, c.created_on, c.last_modified, c.accept_views, c.provide_views, c.quota, c.content_md5, c.target\n"
                 + "FROM node n\n"
                 + "JOIN node c ON c.path ~ (n.path::varchar || ? || '*{1}')::lquery OR c.path = n.path\n"
                 + "WHERE n.node_id = id_from_vos_path(?)\n"
@@ -198,6 +204,9 @@ public class NodeDAO {
             dataNode.setBusy(rs.getBoolean("busy_state"));
             dataNode.setAccepts(getViews(rs.getArray("accept_views")));
             dataNode.setProvides(getViews(rs.getArray("provide_views")));
+        } else if (node instanceof LinkNode) {
+            LinkNode linkNode = (LinkNode) node;
+            linkNode.setTarget(rs.getString("target"));
         }
 
         node.setUri(getUri(rs.getString("vos_path")));
@@ -332,7 +341,7 @@ public class NodeDAO {
         String destName = NodeUtils.getNodeName(destVosPath);
 
         String parentInsert = "INSERT INTO node (node_id, parent_path, parent_relative_path, name, type, location_id, creator_id, group_write, group_read, is_public,\n"
-                + "job_id, tstamp_wrapper_dir, format, async_trans, sticky, accept_views, provide_views, protocols)\n";
+                + "job_id, tstamp_wrapper_dir, format, async_trans, sticky, accept_views, provide_views, protocols, target)\n";
 
         String ctePathPrefix = "SELECT CASE WHEN path::varchar = '' THEN '' ELSE (path::varchar || '.') END AS prefix\n"
                 + "FROM node WHERE node_id = id_from_vos_path(?)";
@@ -341,14 +350,14 @@ public class NodeDAO {
                 + "((SELECT prefix FROM path_prefix) || currval('node_node_id_seq'))::ltree AS new_path,\n"
                 + "path, relative_path, parent_path, parent_relative_path, ? AS name,\n"
                 + "type, location_id, creator_id, group_write, group_read, is_public,\n"
-                + "job_id, tstamp_wrapper_dir, format, async_trans, sticky, accept_views, provide_views, protocols\n"
+                + "job_id, tstamp_wrapper_dir, format, async_trans, sticky, accept_views, provide_views, protocols, target\n"
                 + "FROM node WHERE node_id = id_from_vos_path(?)\n"
                 + "UNION ALL\n"
                 + "SELECT nextval('node_node_id_seq') AS new_node_id,\n"
                 + "(p.new_path::varchar || '.' || currval('node_node_id_seq'))::ltree,\n"
                 + "n.path, n.relative_path, n.parent_path, n.parent_relative_path, n.name,\n"
                 + "n.type, n.location_id, n.creator_id, n.group_write, n.group_read, n.is_public,\n"
-                + "n.job_id, n.tstamp_wrapper_dir, n.format, n.async_trans, n.sticky, n.accept_views, n.provide_views, n.protocols\n"
+                + "n.job_id, n.tstamp_wrapper_dir, n.format, n.async_trans, n.sticky, n.accept_views, n.provide_views, n.protocols, n.target\n"
                 + "FROM node n\n"
                 + "JOIN copied_nodes p ON p.path = n.parent_path";
 
@@ -359,7 +368,7 @@ public class NodeDAO {
                 + "new_node_id, new_parent_path,\n"
                 + "CASE WHEN nlevel(new_parent_path) = rel_offset THEN ''::ltree ELSE subpath(new_parent_path, rel_offset) END new_parent_relative_path,\n"
                 + "name, type, location_id, creator_id, group_write, group_read, is_public,\n"
-                + "job_id, tstamp_wrapper_dir, format, async_trans, sticky, accept_views, provide_views, protocols\n"
+                + "job_id, tstamp_wrapper_dir, format, async_trans, sticky, accept_views, provide_views, protocols, target\n"
                 + "FROM copied_nodes_paths\n";
 
         String sql = parentInsert
@@ -491,7 +500,7 @@ public class NodeDAO {
                 + "async_trans, job_id, creator_id, group_read, "
                 + "group_write, is_public, quota, content_type, content_encoding, "
                 + "content_length, content_md5, created_on, last_modified, "
-                + "accept_views, provide_views, protocols, sticky)\n";
+                + "accept_views, provide_views, protocols, target, sticky)\n";
 
         String deleteSql = "DELETE \n"
                 + "FROM node n\n"
@@ -503,7 +512,7 @@ public class NodeDAO {
                 + "n.async_trans, n.job_id, n.creator_id, n.group_read, "
                 + "n.group_write, n.is_public, n.quota, n.content_type, n.content_encoding, "
                 + "n.content_length, n.content_md5, n.created_on, n.last_modified, "
-                + "n.accept_views, n.provide_views, n.protocols, n.sticky\n";
+                + "n.accept_views, n.provide_views, n.protocols, n.target, n.sticky\n";
 
         String withSql = "WITH del AS (" + deleteSql + ")";