diff --git a/src/cli/args.rs b/src/cli/args.rs
index 1b398fb5e51815846e5856120ec258efca5f9526..44a5d8fde6928665034cc39a5f093fb61caffc62 100644
--- a/src/cli/args.rs
+++ b/src/cli/args.rs
@@ -5,6 +5,7 @@ use crate::util::Uppercase;
 use clap::Parser;
 use std::borrow::Cow;
 use std::ffi::{CStr, CString};
+use std::path::PathBuf;
 use tarantool::auth::AuthMethod;
 use tarantool::log::SayLevel;
 use tarantool::tlua;
@@ -29,7 +30,7 @@ pub const CONFIG_PARAMETERS_ENV: &'static str = "PICODATA_CONFIG_PARAMETERS";
 // Run
 ////////////////////////////////////////////////////////////////////////////////
 
-#[derive(Debug, Parser, tlua::Push, PartialEq)]
+#[derive(Debug, Parser, PartialEq)]
 #[clap(about = "Run the picodata instance")]
 pub struct Run {
     #[clap(long, value_name = "NAME", env = "PICODATA_CLUSTER_ID")]
@@ -43,13 +44,13 @@ pub struct Run {
     /// Here the instance persists all of its data.
     ///
     /// By default this is the current working directory (".").
-    pub data_dir: Option<String>,
+    pub data_dir: Option<PathBuf>,
 
     #[clap(long, value_name = "PATH", env = "PICODATA_CONFIG_FILE")]
     /// Path to configuration file in yaml format.
     ///
     /// By default the "config.yaml" in the data directory is used.
-    pub config: Option<String>,
+    pub config: Option<PathBuf>,
 
     #[clap(
         short = 'c',
@@ -153,7 +154,7 @@ pub struct Run {
     /// At the moment the script is executed, the local storage is
     /// already initialized and HTTP server is running (if specified).
     /// But the raft node is uninitialized yet.
-    pub script: Option<String>,
+    pub script: Option<PathBuf>,
 
     #[clap(long, value_name = "[HOST][:PORT]", env = "PICODATA_HTTP_LISTEN")]
     /// Address to start the HTTP server on. The routing API is exposed
@@ -173,11 +174,11 @@ pub struct Run {
     ///
     /// By default the "admin.sock" in the data directory is used.
     // TODO: rename to admin_socket
-    pub admin_sock: Option<String>,
+    pub admin_sock: Option<PathBuf>,
 
     #[clap(long, value_name = "PATH", env = "PICODATA_PLUGIN_DIR")]
     /// Path to directory with plugin files
-    pub plugin_dir: Option<String>,
+    pub plugin_dir: Option<PathBuf>,
 
     #[clap(long = "tier", value_name = "TIER", env = "PICODATA_INSTANCE_TIER")]
     /// Name of the tier to which the instance will belong.
@@ -196,6 +197,7 @@ pub struct Run {
     pub init_cfg: Option<String>,
 
     #[clap(long = "audit", value_name = "PATH", env = "PICODATA_AUDIT_LOG")]
+    // As it's not always a path the value type is left as `String`.
     /// Configuration for the audit log.
     /// Valid options:
     ///
@@ -219,6 +221,7 @@ pub struct Run {
     pub shredding: bool,
 
     #[clap(long = "log", value_name = "PATH", env = "PICODATA_LOG")]
+    // As it's not always a path the value type is left as `String`.
     /// Configuration for the picodata diagnostic log.
     /// Valid options:
     ///
@@ -250,7 +253,7 @@ pub struct Run {
     /// Path to a plain-text file with a password for the system user "pico_service".
     /// This password will be used for internal communication among instances of
     /// picodata, so it must be the same on all instances.
-    pub service_password_file: Option<String>,
+    pub service_password_file: Option<PathBuf>,
 }
 
 // Copy enum because clap:ArgEnum can't be derived for the foreign SayLevel.
diff --git a/src/cli/run.rs b/src/cli/run.rs
index 6891431fb6562d8adadba7b4ae0e3f82f4204c3c..e435bb23ec4a84a12a85e693e250c855e6f38c76 100644
--- a/src/cli/run.rs
+++ b/src/cli/run.rs
@@ -1,3 +1,5 @@
+use std::path::Path;
+
 use nix::sys::signal;
 use nix::sys::termios::{tcgetattr, tcsetattr, SetArg::TCSADRAIN};
 use nix::sys::wait::{waitpid, WaitStatus};
@@ -204,7 +206,7 @@ pub fn main(args: args::Run) -> ! {
     }
 }
 
-fn rm_tarantool_files(data_dir: &str) {
+fn rm_tarantool_files(data_dir: impl AsRef<Path>) {
     std::fs::read_dir(data_dir)
         .expect("[supervisor] failed reading data_dir")
         .map(|entry| entry.expect("[supervisor] failed reading directory entry"))
diff --git a/src/config.rs b/src/config.rs
index 07d792bef491f97b37f8c9ad94dd951bcbef70d3..7f49b83b5f1c5859f48eaf8b267feec0e349fee7 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -21,6 +21,7 @@ use serde_yaml::Value as YamlValue;
 use std::collections::HashMap;
 use std::io::Write;
 use std::path::Path;
+use std::path::PathBuf;
 use tarantool::log::SayLevel;
 use tarantool::tlua;
 
@@ -77,13 +78,13 @@ impl PicodataConfig {
         validate_args(&args)?;
 
         let cwd = std::env::current_dir();
-        let cwd = cwd.as_deref().unwrap_or_else(|_| Path::new(".")).display();
-        let default_path = format!("{cwd}/{DEFAULT_CONFIG_FILE_NAME}");
+        let cwd = cwd.as_deref().unwrap_or_else(|_| Path::new("."));
+        let default_path = cwd.join(DEFAULT_CONFIG_FILE_NAME);
         let args_path = args.config.clone().map(|path| {
-            if Path::new(&path).is_absolute() {
+            if path.is_absolute() {
                 path
             } else {
-                format!("{cwd}/{path}")
+                cwd.join(path)
             }
         });
 
@@ -91,6 +92,8 @@ impl PicodataConfig {
         match (&args_path, file_exists(&default_path)) {
             (Some(args_path), true) => {
                 if args_path != &default_path {
+                    let cwd = cwd.display();
+                    let args_path = args_path.display();
                     #[rustfmt::skip]
                     tlog!(Warning, "A path to configuration file '{args_path}' was provided explicitly,
 but a '{DEFAULT_CONFIG_FILE_NAME}' file in the current working directory '{cwd}' also exists.
@@ -104,7 +107,7 @@ Using configuration file '{args_path}'.");
             }
             (None, true) => {
                 #[rustfmt::skip]
-                tlog!(Info, "Reading configuration file '{DEFAULT_CONFIG_FILE_NAME}' in the current working directory '{cwd}'.");
+                tlog!(Info, "Reading configuration file '{DEFAULT_CONFIG_FILE_NAME}' in the current working directory '{}'.", cwd.display());
                 config_from_file = Some((Self::read_yaml_file(&default_path)?, &default_path));
             }
             (None, false) => {}
@@ -185,9 +188,13 @@ Using configuration file '{args_path}'.");
     }
 
     #[inline]
-    pub fn read_yaml_file(path: &str) -> Result<Box<Self>, Error> {
-        let contents = std::fs::read_to_string(path)
-            .map_err(|e| Error::other(format!("can't read from '{path}': {e}")))?;
+    pub fn read_yaml_file(path: impl AsRef<Path>) -> Result<Box<Self>, Error> {
+        let contents = std::fs::read_to_string(path.as_ref()).map_err(|e| {
+            Error::other(format!(
+                "can't read from '{}': {e}",
+                path.as_ref().display()
+            ))
+        })?;
         Self::read_yaml_contents(&contents)
     }
 
@@ -805,14 +812,14 @@ impl ClusterConfig {
 
 #[derive(PartialEq, Default, Debug, Clone, serde::Deserialize, serde::Serialize, Introspection)]
 pub struct InstanceConfig {
-    #[introspection(config_default = ".")]
-    pub data_dir: Option<String>,
-    pub service_password_file: Option<String>,
+    #[introspection(config_default = PathBuf::from("."))]
+    pub data_dir: Option<PathBuf>,
+    pub service_password_file: Option<PathBuf>,
 
     // Skip serializing, so that default config doesn't contain this option,
     // which isn't allowed to be set from file.
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub config_file: Option<String>,
+    pub config_file: Option<PathBuf>,
 
     // Skip serializing, so that default config doesn't contain duplicate
     // cluster_id fields.
@@ -851,17 +858,17 @@ pub struct InstanceConfig {
 
     pub http_listen: Option<Address>,
 
-    #[introspection(config_default = format!("{}/admin.sock", self.data_dir.as_ref().unwrap()))]
-    pub admin_socket: Option<String>,
+    #[introspection(config_default = self.data_dir.as_ref().map(|dir| dir.join("admin.sock")))]
+    pub admin_socket: Option<PathBuf>,
 
     // TODO:
     // - sepparate config file for common parameters
-    pub plugin_dir: Option<String>,
+    pub plugin_dir: Option<PathBuf>,
 
     // Skip serializing, so that default config doesn't contain this option,
     // because it's deprecated.
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub deprecated_script: Option<String>,
+    pub deprecated_script: Option<PathBuf>,
 
     pub audit: Option<String>,
 
@@ -904,7 +911,7 @@ pub struct InstanceConfig {
 // TODO: remove all of the .clone() calls from these methods
 impl InstanceConfig {
     #[inline]
-    pub fn data_dir(&self) -> String {
+    pub fn data_dir(&self) -> PathBuf {
         self.data_dir
             .clone()
             .expect("is set in PicodataConfig::set_defaults_explicitly")
@@ -956,7 +963,7 @@ impl InstanceConfig {
     }
 
     #[inline]
-    pub fn admin_socket(&self) -> String {
+    pub fn admin_socket(&self) -> PathBuf {
         self.admin_socket
             .clone()
             .expect("is set in PicodataConfig::set_defaults_explicitly")
@@ -1706,7 +1713,7 @@ instance:
             assert_eq!(config.instance.log.level.unwrap(), args::LogLevel::Debug);
             assert_eq!(config.instance.memtx.memory.unwrap(), 0xdead_beef);
             assert_eq!(config.instance.audit.unwrap(), "audit.txt");
-            assert_eq!(config.instance.data_dir.unwrap(), ".");
+            assert_eq!(config.instance.data_dir.unwrap(), PathBuf::from("."));
 
             //
             // Errors
diff --git a/src/lib.rs b/src/lib.rs
index 816184d3fec5d97cdcfd818aa74f62eb41d63278..ff26f67408848304e108cc987615b64fb926296c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,7 +7,7 @@
 #![allow(clippy::redundant_static_lifetimes)]
 #![allow(clippy::vec_init_then_push)]
 use serde::{Deserialize, Serialize};
-use std::path::{Path, PathBuf};
+use std::path::Path;
 
 use ::raft::prelude as raft;
 use ::tarantool::error::Error as TntError;
@@ -562,7 +562,7 @@ fn init_common(
     std::fs::create_dir_all(config.instance.data_dir()).unwrap();
 
     if let Some(log_config) = &cfg.log {
-        tlog!(Info, "switching to log configuration: {log_config}");
+        tlog!(Info, "switching to log configuration: {}", log_config);
     }
     // See doc comments in tlog.rs for explanation.
     tlog::set_core_logger_is_initialized(true);
@@ -894,8 +894,14 @@ fn postjoin(
     // Execute postjoin script if present
     if let Some(ref script) = config.instance.deprecated_script {
         let l = ::tarantool::lua_state();
-        l.exec_with("dofile(...)", script)
-            .unwrap_or_else(|err| panic!("failed to execute postjoin script: {err}"))
+        l.exec_with(
+            "dofile(...)",
+            script.to_str().ok_or(Error::other(format!(
+                "postjoin script path {} is not encoded in UTF-8",
+                script.to_string_lossy()
+            )))?,
+        )
+        .unwrap_or_else(|err| panic!("failed to execute postjoin script: {err}"))
     }
 
     // Reset the quorum BEFORE initializing the raft node.
@@ -921,7 +927,7 @@ fn postjoin(
         .expect("changing listen port shouldn't fail");
 
     // Start admin console
-    let socket_uri = util::validate_and_complete_unix_socket_path(&config.instance.admin_socket())?;
+    let socket_uri = util::validate_and_complete_unix_socket_path(config.instance.admin_socket())?;
     let lua = ::tarantool::lua_state();
     lua.exec_with(r#"require('console').listen(...)"#, &socket_uri)?;
 
@@ -1038,7 +1044,7 @@ fn postjoin(
 
     let pg_config = &config.instance.pg;
     if pg_config.enabled() {
-        pgproto::start(pg_config, PathBuf::from(config.instance.data_dir()))?;
+        pgproto::start(pg_config, config.instance.data_dir())?;
     }
 
     Ok(())
diff --git a/src/pico_service.rs b/src/pico_service.rs
index c1a80b9537731f8b1558e6d362c6cdd252acd3e4..512baccde120715cc632def90f8bd249238112a0 100644
--- a/src/pico_service.rs
+++ b/src/pico_service.rs
@@ -4,6 +4,7 @@ use crate::unwrap_ok_or;
 use std::fs::File;
 use std::io::Read;
 use std::os::unix::fs::PermissionsExt as _;
+use std::path::Path;
 
 /// Password of the special system user "pico_service".
 ///
@@ -19,12 +20,14 @@ pub(crate) fn pico_service_password() -> &'static str {
     unsafe { PICO_SERVICE_PASSWORD.as_deref() }.unwrap_or("")
 }
 
-pub(crate) fn read_pico_service_password_from_file(filename: &str) -> Result<(), Error> {
-    let res = read_file_contents_and_mode(filename);
+pub(crate) fn read_pico_service_password_from_file(
+    filename: impl AsRef<Path>,
+) -> Result<(), Error> {
+    let res = read_file_contents_and_mode(filename.as_ref());
     let (data, mode) = unwrap_ok_or!(
         res,
         Err(e) => {
-            return Err(Error::other(format!("failed to read password from file '{filename}': {e}")));
+            return Err(Error::other(format!("failed to read password from file '{}': {e}", filename.as_ref().display())));
         }
     );
 
@@ -50,7 +53,7 @@ pub(crate) fn read_pico_service_password_from_file(filename: &str) -> Result<(),
     Ok(())
 }
 
-fn read_file_contents_and_mode(filename: &str) -> std::io::Result<(Vec<u8>, u32)> {
+fn read_file_contents_and_mode(filename: impl AsRef<Path>) -> std::io::Result<(Vec<u8>, u32)> {
     let mut file = File::open(filename)?;
     let metadata = file.metadata()?;
     let mode = metadata.permissions().mode();
diff --git a/src/tarantool.rs b/src/tarantool.rs
index 6f237c3cf1f2f54d827194a6aadac98805ef87b7..71b65e68c92a14751f0a1bd28da3dbb0116dfefb 100644
--- a/src/tarantool.rs
+++ b/src/tarantool.rs
@@ -15,6 +15,7 @@ use file_shred::*;
 use std::collections::HashMap;
 use std::ffi::CStr;
 use std::os::unix::ffi::OsStrExt;
+use std::path::PathBuf;
 use tlua::CallError;
 
 #[macro_export]
@@ -100,9 +101,9 @@ pub struct Cfg {
     pub log: Option<String>,
     pub log_level: Option<u8>,
 
-    pub wal_dir: String,
-    pub memtx_dir: String,
-    pub vinyl_dir: String,
+    pub wal_dir: PathBuf,
+    pub memtx_dir: PathBuf,
+    pub vinyl_dir: PathBuf,
 
     #[serde(flatten)]
     pub other_fields: HashMap<String, rmpv::Value>,
diff --git a/src/util.rs b/src/util.rs
index 6556162fcbb6fc1fe7eff5a6801b6c9e24a821a7..6d38ad0ef93a55ce6cd8a2ec75a443c14e646694 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -8,6 +8,7 @@ use std::io::Write as _;
 use std::mem::replace;
 use std::os::fd::AsRawFd;
 use std::panic::Location;
+use std::path::Path;
 use std::time::Duration;
 use tarantool::session::{self, UserId};
 pub use Either::{Left, Right};
@@ -503,24 +504,29 @@ pub fn prompt_password(prompt: &str) -> Result<String, std::io::Error> {
 /// Non-absolute paths are prepended with `./`.
 ///
 /// Returns and error in case validation using lua `uri` module fails.
-pub fn validate_and_complete_unix_socket_path(socket_path: &str) -> Result<String, Error> {
+pub fn validate_and_complete_unix_socket_path(
+    socket_path: impl AsRef<Path>,
+) -> Result<String, Error> {
     let l = ::tarantool::lua_state();
-    let path = std::path::Path::new(socket_path);
-    let console_sock = match path.components().next() {
-        Some(std::path::Component::Normal(_)) | Some(std::path::Component::ParentDir) => {
-            format!("unix/:./{socket_path}")
-        }
-        _ => format!("unix/:{socket_path}"),
+    let path = socket_path.as_ref();
+    let path_str = path.to_str().ok_or(Error::other(format!(
+        "socket_path {} is not encoded in UTF-8",
+        socket_path.as_ref().to_string_lossy()
+    )))?;
+    let path_str = if path.is_absolute() {
+        format!("unix/:{path_str}")
+    } else {
+        format!("unix/:./{path_str}")
     };
 
     // Check that Lua can correctly parse the unix socket path
     l.exec_with(
         "local u = require('uri').parse(...); assert(u and u.unix)",
-        &console_sock,
+        &path_str,
     )
-    .map_err(|_| Error::other(format!("invalid socket path: {socket_path}")))?;
+    .map_err(|_| Error::other(format!("invalid socket path: {}", path.display())))?;
 
-    Ok(console_sock)
+    Ok(path_str)
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -820,7 +826,7 @@ impl<'a> Lexer<'a> {
 ////////////////////////////////////////////////////////////////////////////////
 
 #[inline(always)]
-pub fn file_exists(path: &str) -> bool {
+pub fn file_exists(path: impl AsRef<Path>) -> bool {
     std::fs::metadata(path).is_ok()
 }