diff --git a/src/bootstrap_entries.rs b/src/bootstrap_entries.rs
index f0931a3748d8ee8c0e0097becae55e1c75168ead..a0c57e9e908bde0afaf2af1cfd329f157ca8b189 100644
--- a/src/bootstrap_entries.rs
+++ b/src/bootstrap_entries.rs
@@ -6,6 +6,7 @@ use tarantool::auth::AuthMethod;
 
 use crate::cli::args;
 use crate::instance::Instance;
+use crate::schema;
 use crate::schema::PrivilegeDef;
 use crate::schema::RoleDef;
 use crate::schema::TableDef;
@@ -163,6 +164,12 @@ pub(super) fn prepare(args: &args::Run, instance: &Instance, tiers: &[Tier]) ->
         ADMIN_ID,
     ));
 
+    init_entries_push_op(op::Dml::insert(
+        ClusterwideTable::User,
+        schema::pico_service_user_def(),
+        ADMIN_ID,
+    ));
+
     // equivalent SQL expression: CREATE ROLE 'public'
     init_entries_push_op(op::Dml::insert(
         ClusterwideTable::Role,
@@ -200,6 +207,15 @@ pub(super) fn prepare(args: &args::Run, instance: &Instance, tiers: &[Tier]) ->
         ));
     }
 
+    // Grant all privileges on "universe" to "pico_service".
+    for priv_def in schema::pico_service_privilege_defs() {
+        init_entries_push_op(op::Dml::insert(
+            ClusterwideTable::Privilege,
+            priv_def,
+            ADMIN_ID,
+        ));
+    }
+
     // Builtin global table definitions
     for table_def in TableDef::system_tables() {
         init_entries_push_op(op::Dml::insert(
diff --git a/src/cli/test.rs b/src/cli/test.rs
index 50e29ec2a7d780690efb5ff847d47639e3906923..750c2e98d8f0560f7b93978b6778f93f644eb990 100644
--- a/src/cli/test.rs
+++ b/src/cli/test.rs
@@ -141,6 +141,8 @@ fn test_one(test: &TestCase) {
     };
 
     tarantool::set_cfg(&cfg);
+
+    crate::schema::init_user_pico_service();
     tarantool::exec(
         r#"
         box.schema.user.grant('guest', 'super', nil, nil, {if_not_exists = true})
diff --git a/src/lib.rs b/src/lib.rs
index 760b511a31f43ded3fad23fe4b2306c96d5d781d..a2b75c51a906d21478385b4d86cb4be6a0e221af 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -500,6 +500,8 @@ fn init_common(args: &args::Run, cfg: &tarantool::Cfg) -> (Clusterwide, RaftSpac
     init_handlers();
 
     let storage = Clusterwide::try_get(true).expect("storage initialization should never fail");
+    schema::init_user_pico_service();
+
     set_login_attempts_check(storage.clone());
     set_on_access_denied_audit_trigger();
     let raft_storage =
diff --git a/src/schema.rs b/src/schema.rs
index 13589b46c89423045c5b83d1e557ad45f573ea7d..333080844dc8b4fe9d74a31f0ad7ccc272c536bb 100644
--- a/src/schema.rs
+++ b/src/schema.rs
@@ -5,6 +5,7 @@ use std::fmt::Display;
 use std::time::Duration;
 
 use tarantool::auth::AuthDef;
+use tarantool::auth::AuthMethod;
 use tarantool::error::TarantoolError;
 use tarantool::error::TarantoolErrorCode;
 use tarantool::fiber;
@@ -26,6 +27,7 @@ use tarantool::{
 use serde::{Deserialize, Serialize};
 
 use crate::cas::{self, compare_and_swap};
+use crate::storage;
 use crate::storage::{Clusterwide, SPACE_ID_INTERNAL_MAX};
 use crate::storage::{ClusterwideTable, PropertyName};
 use crate::traft::error::Error;
@@ -376,6 +378,13 @@ pub const ADMIN_ID: UserId = 1;
 /// See also <https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/_user/#box-space-user>
 pub const PUBLIC_ID: UserId = 2;
 
+/// User id of the builtin role "replication".
+///
+/// Role "replication" has the following grants:
+/// - Read access to the "universe"
+/// - Write access to the space "_cluster"
+pub const ROLE_REPLICATION_ID: i64 = 3;
+
 /// User id of the builtin role "super".
 ///
 /// Users with this role have access to everything.
@@ -383,6 +392,13 @@ pub const PUBLIC_ID: UserId = 2;
 /// See also <https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/_user/#box-space-user>
 pub const SUPER_ID: UserId = 31;
 
+/// User id of the builtin user "pico_service".
+///
+/// A special user for internal communication between instances of picodata.
+/// It is equivalent in it's privileges to "admin". The only difference is that
+/// only the automated rpc calls are performed as "pico_service".
+pub const PICO_SERVICE_ID: UserId = 32;
+
 /// Object id of the special builtin object "universe".
 ///
 /// Object "universe" is basically an alias to the "whole database".
@@ -679,6 +695,113 @@ impl PrivilegeDef {
     }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// init_pico_service
+////////////////////////////////////////////////////////////////////////////////
+
+/// Name of the special builtin user for internal communication between
+/// instances. It's id is [`PICO_SERVICE_ID`].
+///
+/// Use this constant instead of literal "pico_service" so that it's easier to
+/// find all the places where we refer to "pico_service".
+pub const PICO_SERVICE_USER_NAME: &'static str = "pico_service";
+
+#[inline]
+pub fn pico_service_user_def() -> &'static UserDef {
+    static PICO_SERVICE_USER_DEF: OnceCell<UserDef> = OnceCell::new();
+    PICO_SERVICE_USER_DEF.get_or_init(|| UserDef {
+        id: PICO_SERVICE_ID,
+        name: PICO_SERVICE_USER_NAME.into(),
+        // This means the local schema is already up to date and main loop doesn't need to do anything
+        schema_version: INITIAL_SCHEMA_VERSION,
+        auth: AuthDef::new(
+            AuthMethod::ChapSha1,
+            tarantool::auth::AuthData::new(&AuthMethod::ChapSha1, PICO_SERVICE_USER_NAME, "")
+                .into_string(),
+        ),
+        owner: ADMIN_ID,
+    })
+}
+
+#[inline]
+pub fn pico_service_privilege_defs() -> &'static [PrivilegeDef] {
+    static PICO_SERVICE_PRIVILEGE_DEFS: OnceCell<Vec<PrivilegeDef>> = OnceCell::new();
+    PICO_SERVICE_PRIVILEGE_DEFS.get_or_init(|| {
+        let mut res = Vec::with_capacity(PrivilegeType::VARIANTS.len());
+
+        for &privilege in PrivilegeType::VARIANTS {
+            res.push(PrivilegeDef {
+                privilege,
+                object_type: SchemaObjectType::Universe,
+                object_id: UNIVERSE_ID,
+                grantee_id: PICO_SERVICE_ID,
+                grantor_id: ADMIN_ID,
+                // This means the local schema is already up to date and main loop doesn't need to do anything
+                schema_version: INITIAL_SCHEMA_VERSION,
+            });
+        }
+
+        // TODO: explain
+        res.push(PrivilegeDef {
+            privilege: PrivilegeType::Execute,
+            object_type: SchemaObjectType::Role,
+            object_id: ROLE_REPLICATION_ID,
+            grantee_id: PICO_SERVICE_ID,
+            grantor_id: ADMIN_ID,
+            // This means the local schema is already up to date and main loop doesn't need to do anything
+            schema_version: INITIAL_SCHEMA_VERSION,
+        });
+
+        res
+    })
+}
+
+pub fn init_user_pico_service() {
+    let sys_user = SystemSpace::User.as_space();
+    let sys_priv = SystemSpace::Priv.as_space();
+
+    let t = sys_user
+        .get(&[PICO_SERVICE_ID])
+        .expect("reading from _user shouldn't fail");
+    if t.is_some() {
+        // Already exists (instance restarted)
+        return;
+    }
+
+    let user_def = pico_service_user_def();
+    let res = storage::acl::on_master_create_user(user_def, false);
+    if let Err(e) = res {
+        panic!("failed creating user '{PICO_SERVICE_USER_NAME}': {e}");
+    }
+
+    // Grant ALL privileges to "every object of every type".
+    const PRIVILEGE_ALL: u32 = 0xffff_ffff;
+    let res = sys_priv.insert(&(
+        ADMIN_ID,
+        PICO_SERVICE_ID,
+        "universe",
+        UNIVERSE_ID,
+        PRIVILEGE_ALL,
+    ));
+    if let Err(e) = res {
+        panic!("failed creating user '{PICO_SERVICE_USER_NAME}': {e}");
+    }
+
+    // Also grant role "replication", because all privileges to "every object of
+    // every type" is not enough.
+    const PRIVILEGE_EXECUTE: u32 = 4;
+    let res = sys_priv.insert(&(
+        ADMIN_ID,
+        PICO_SERVICE_ID,
+        "role",
+        ROLE_REPLICATION_ID,
+        PRIVILEGE_EXECUTE,
+    ));
+    if let Err(e) = res {
+        panic!("failed creating user '{PICO_SERVICE_USER_NAME}': {e}");
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // ...
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/test/int/test_basics.py b/test/int/test_basics.py
index f84e05e985b07e8dfda2ef586882de13e57fb396..76e3ad36335e104b23f71b4ba76beabd8b97b398 100644
--- a/test/int/test_basics.py
+++ b/test/int/test_basics.py
@@ -266,11 +266,20 @@ def test_raft_log(instance: Instance):
 |  0  | 1  |Insert({_pico_property}, ["snapshot_read_view_close_timeout",86400.0])|
 |  0  | 1  |Insert({_pico_user}, [0,"guest",0,["chap-sha1","vhvewKp0tNyweZQ+cFKAlsyphfg="],1])|
 |  0  | 1  |Insert({_pico_user}, [1,"admin",0,["chap-sha1",""],1])|
+|  0  | 1  |Insert({_pico_user}, [32,"pico_service",0,["chap-sha1","vhvewKp0tNyweZQ+cFKAlsyphfg="],1])|
 |  0  | 1  |Insert({_pico_role}, [2,"public",0,1])|
 |  0  | 1  |Insert({_pico_role}, [31,"super",0,1])|
 |  0  | 1  |Insert({_pico_privilege}, ["login","universe",0,0,1,0])|
 |  0  | 1  |Insert({_pico_privilege}, ["login","universe",0,1,1,0])|
 |  0  | 1  |Insert({_pico_privilege}, ["execute","role",2,0,1,0])|
+|  0  | 1  |Insert({_pico_privilege}, ["read","universe",0,32,1,0])|
+|  0  | 1  |Insert({_pico_privilege}, ["write","universe",0,32,1,0])|
+|  0  | 1  |Insert({_pico_privilege}, ["execute","universe",0,32,1,0])|
+|  0  | 1  |Insert({_pico_privilege}, ["login","universe",0,32,1,0])|
+|  0  | 1  |Insert({_pico_privilege}, ["create","universe",0,32,1,0])|
+|  0  | 1  |Insert({_pico_privilege}, ["drop","universe",0,32,1,0])|
+|  0  | 1  |Insert({_pico_privilege}, ["alter","universe",0,32,1,0])|
+|  0  | 1  |Insert({_pico_privilege}, ["execute","role",3,32,1,0])|
 |  0  | 1  |Insert({_pico_table}, [{_pico_table},"_pico_table",["global"],[["id","unsigned",false],["name","string",false],["distribution","array",false],["format","array",false],["schema_version","unsigned",false],["operable","boolean",false],["engine","string",false],["owner","unsigned",false]],0,true,"memtx",1])|
 |  0  | 1  |Insert({_pico_table}, [{_pico_index},"_pico_index",["global"],[["table_id","unsigned",false],["id","unsigned",false],["name","string",false],["local","boolean",false],["parts","array",false],["schema_version","unsigned",false],["operable","boolean",false],["unique","boolean",false]],0,true,"memtx",1])|
 |  0  | 1  |Insert({_pico_table}, [{_pico_peer_address},"_pico_peer_address",["global"],[["raft_id","unsigned",false],["address","string",false]],0,true,"memtx",1])|
@@ -287,8 +296,8 @@ def test_raft_log(instance: Instance):
 |  0  | 2  |Insert({_pico_replicaset}, ["r1","e0df68c5-e7f9-395f-86b3-30ad9e1b7b07","i1","i1","default",0.0,"auto","not-ready"])|
 |  0  | 2  |Replace({_pico_instance}, ["i1","68d4a766-4144-3248-aeb4-e212356716e4",1,"r1","e0df68c5-e7f9-395f-86b3-30ad9e1b7b07",["Replicated",1],["Online",1],{b},"default"])|
 |  0  | 2  |Update({_pico_replicaset}, ["r1"], [["=","weight",1.0], ["=","state","ready"]])|
-|  0  | 2  |Replace({_pico_property}, ["target_vshard_config",[{{"e0df68c5-e7f9-395f-86b3-30ad9e1b7b07":[{{"68d4a766-4144-3248-aeb4-e212356716e4":["guest:@127.0.0.1:{p}","i1",true]}},1.0]}},"on"]])|
-|  0  | 2  |Replace({_pico_property}, ["current_vshard_config",[{{"e0df68c5-e7f9-395f-86b3-30ad9e1b7b07":[{{"68d4a766-4144-3248-aeb4-e212356716e4":["guest:@127.0.0.1:{p}","i1",true]}},1.0]}},"on"]])|
+|  0  | 2  |Replace({_pico_property}, ["target_vshard_config",[{{"e0df68c5-e7f9-395f-86b3-30ad9e1b7b07":[{{"68d4a766-4144-3248-aeb4-e212356716e4":["pico_service:@127.0.0.1:{p}","i1",true]}},1.0]}},"on"]])|
+|  0  | 2  |Replace({_pico_property}, ["current_vshard_config",[{{"e0df68c5-e7f9-395f-86b3-30ad9e1b7b07":[{{"68d4a766-4144-3248-aeb4-e212356716e4":["pico_service:@127.0.0.1:{p}","i1",true]}},1.0]}},"on"]])|
 |  0  | 2  |Replace({_pico_property}, ["vshard_bootstrapped",true])|
 |  0  | 2  |Replace({_pico_instance}, ["i1","68d4a766-4144-3248-aeb4-e212356716e4",1,"r1","e0df68c5-e7f9-395f-86b3-30ad9e1b7b07",["Online",1],["Online",1],{b},"default"])|
 +-----+----+--------+