VS Code Automated Deployment to Linux: Non-Root User, SSH Keys & Systemd in Practice — Boost Efficiency and Security

Manually deploying an application to a remote server can be a chore. By automating the process, you save time, avoid repetitive steps, and stay focused on coding. In this guide we’ll set up a convenient workflow where Visual Studio Code (VS Code) tasks automatically build your app, transfer files securely to a remote Linux server using SSH key authentication (through a non-root user), and restart the app with a systemd service. This not only streamlines deployment but also increases security by eliminating root logins and password-based SSH. Using a dedicated, unprivileged user adds another barrier between attackers and your server, and relying on keys instead of passwords all but removes brute-force risk on SSH. Let’s dive into the step-by-step setup.

1. Create a Dedicated Non-Root User for Deployment

Deploying or running an app as root is bad practice, so we’ll create a new user (e.g. vscodeuser) on the remote server to own and run our app. A non-root account limits damage if it’s compromised and is generally recommended. This user will be used for SSH logins and to run the app’s service.

Log in to the remote server as root (or any sudo-capable admin) and add the new user (replace vscodeuser with the username you prefer):

sudo adduser vscodeuser

This prompts for a password and user details. Pick a strong password (though we’ll switch to key-based logins shortly, so SSH won’t ask for it). Accept the defaults for user info if you like. A new account and home directory (e.g. /home/vscodeuser) are created.

Optional: If you want this user to perform admin tasks, add it to the sudo group (Debian/Ubuntu):

sudo usermod -aG sudo vscodeuser

For a pure deploy user, sudo isn’t usually necessary.

Why not root? Every Unix/Linux system has a root account, and attackers routinely target it. Disabling direct root SSH and using an ordinary user means an attacker must compromise a non-root account first, then escalate privileges — an extra hurdle. Completely disabling root SSH logins and using an unprivileged, key-authenticated account is widely considered best practice.

2. Set Up SSH Key Authentication for the New User

Next we’ll enable SSH public-key authentication for vscodeuser so we can log in without passwords. Key-based logins are not only more convenient (no prompts in our automation) but also more secure, especially if password logins are disabled.

On your local machine

You need an SSH key pair (a private key and a public key). If you already have one you’d like to use, skip generating a new key; otherwise generate one in step 4.

On the remote server

Authorize your public key for the new user:

  1. Create the .ssh directory in the new user’s home (and lock down its permissions). As root (or via sudo):

    sudo mkdir -p /home/vscodeuser/.ssh
    sudo chmod 700 /home/vscodeuser/.ssh
    
  2. Add your public key to authorized_keys: If you have the key text handy, append it manually, for example:

    sudo sh -c 'echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLVDBIpdpfePg/...(rest of key)...fRjB user@local" > /home/vscodeuser/.ssh/authorized_keys'
    

    (Replace the ssh-rsa string with your actual key, all on one line.) Or open the file with sudo nano /home/vscodeuser/.ssh/authorized_keys and paste the key.

  3. Fix permissions and ownership:

    sudo chmod 600 /home/vscodeuser/.ssh/authorized_keys
    sudo chown -R vscodeuser:vscodeuser /home/vscodeuser/.ssh
    

    authorized_keys must be 600 and owned by the user. SSH will ignore it otherwise.

Your server now allows vscodeuser to log in with a key. This replicates what ssh-copy-id does: create ~/.ssh, add the key, fix perms. If ssh-copy-id is available locally, run:

# On your local machine:
ssh-copy-id vscodeuser@your_server_ip

It prompts once for the user’s password, then installs the key properly. Afterwards you should SSH with no password prompt.

Security tip: Since vscodeuser is key-only, consider disabling password auth and prohibiting root logins in /etc/ssh/sshd_config by setting PasswordAuthentication no and PermitRootLogin no, then restarting SSH. All access must then use keys, thwarting brute-force attempts and blocking root entirely. Log in as vscodeuser and use sudo only when necessary.

3. Verify SSH Permissions and Ownership

Double-check the SSH setup — it’s a frequent source of errors. On the remote server (as root or sudo):

  • /home/vscodeuser/.ssh must be owned by vscodeuser and drwx------ (700).
  • /home/vscodeuser/.ssh/authorized_keys must be owned by vscodeuser and -rw------- (600).

Verify:

ls -l /home/vscodeuser/.ssh
ls -l /home/vscodeuser/.ssh/authorized_keys

You should see something like:

drwx------ 2 vscodeuser vscodeuser 4096 May 24 00:30 .ssh
-rw------- 1 vscodeuser vscodeuser  382 May 24 00:30 authorized_keys

Fix anything with chmod and chown as above. StrictModes in SSH will reject loose perms.

Now test from your local machine (you may need a key first — see next step):

ssh vscodeuser@your_server_ip

You should log in without a password prompt. If prompted, key auth failed — re-check earlier steps.

4. Generate and Use an SSH Key Pair Locally

If you don’t already have a key pair on your dev box, create one now. The private key stays local (guard it!), the public key is what you installed on the server.

On your local machine:

ssh-keygen -t rsa -b 4096 -C "VSCode Deploy Key"
  • -t rsa -b 4096: RSA key, 4096 bits.
  • -C "VSCode Deploy Key": a label.

Accept the default path (~/.ssh/id_rsa / id_rsa.pub) or choose something like ~/.ssh/id_vscode_deploy. Enter a passphrase for extra security, or press Enter for none (but then anyone with the file can use it). If you set a passphrase, use an SSH agent so automation runs without prompts.

You’ll now have:

  • ~/.ssh/id_rsaprivate key (keep secret).
  • ~/.ssh/id_rsa.pubpublic key (copy to server).

If this is a new key, go back to Step 2 and add id_rsa.pub to the server. Then test:

ssh vscodeuser@your_server_ip -i ~/.ssh/id_rsa

(or omit -i if it’s your default key).

Example key formats:

  • Public (id_rsa.pub)

    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCxqHYEE3XdexSmo5rT01nxOKjP4YOIuC/... demo@local
    
  • Private (id_rsa)

    -----BEGIN OPENSSH PRIVATE KEY-----
    b3BlbnN...
    -----END OPENSSH PRIVATE KEY-----
    

Add the key to your agent (ssh-add ~/.ssh/id_rsa) so VS Code can use it without prompting.

Key-based SSH for vscodeuser now works; time to set up the app under that user and automate deployment.

5. Create a Systemd Service (User Service) for Your App

Instead of running the app by hand, create a systemd service. It can start at boot, restart on crash, and be controlled with one command. We’ll make a user service under vscodeuser, so no root is required.

As vscodeuser on the remote server:

  1. Create the unit file:

    mkdir -p ~/.config/systemd/user
    nano ~/.config/systemd/user/myapp.service
    
  2. Define the service:

    [Unit]
    Description=My App Service
    After=network.target
    
    [Service]
    ExecStart=/home/vscodeuser/myapp/myappbinary
    WorkingDirectory=/home/vscodeuser/myapp
    Restart=on-failure
    # No User= needed; this is a user service
    
    [Install]
    WantedBy=default.target
    
    • ExecStart: command to run your app. Adjust path.
    • WorkingDirectory: (optional) app folder.
    • Restart: restart on crashes.
  3. Reload systemd and enable:

    systemctl --user daemon-reload
    systemctl --user enable myapp.service --now
    

    Check status:

    systemctl --user status myapp.service
    
  4. Enable lingering (so the service survives logouts/reboots):

    sudo loginctl enable-linger vscodeuser
    

Now vscodeuser can control the service:

systemctl --user restart myapp.service

We’ll automate that next.

6. Automate Build & Deployment with VS Code Tasks

Here’s the magic: configure VS Code to build, deploy, and restart with a single command or keybinding. VS Code’s task runner can call external tools/scripts.

Open .vscode/tasks.json in your workspace:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Build App",
            "type": "shell",
            "command": "go build -o myapp .",
            "group": { "kind": "build", "isDefault": true },
            "problemMatcher": []
        },
        {
            "label": "Deploy App",
            "type": "shell",
            "command": "rsync -avz ./myapp vscodeuser@your-server-ip:/home/vscodeuser/myapp/",
            "dependsOn": "Build App",
            "problemMatcher": []
        },
        {
            "label": "Restart App Service",
            "type": "shell",
            "command": "ssh vscodeuser@your-server-ip 'systemctl --user restart myapp.service'",
            "dependsOn": "Deploy App",
            "problemMatcher": []
        }
    ]
}

How it works

  • Build App: compiles your project (example for Go). Adjust for Node, Java, Rust, etc.
  • Deploy App: rsync syncs the artifact(s) to the server. Add --delete if you want to remove stale files. rsync will auto-use your key or any agent.
  • Restart App Service: SSH in and restart systemd. It runs only if deploy succeeds.

Run “Tasks: Run Task” → Restart App Service. VS Code chains Build ➝ Deploy ➝ Restart. Bind a key or use Ctrl+Shift+B if Build is grouped.

Note: Tasks assume a non-interactive environment. With key auth, no password prompts occur. If your key has a passphrase, make sure an agent caches it; otherwise tasks may hang waiting for input.

Summary

With this setup you can:

  1. Build/compile your project.
  2. Securely copy updated files to the server (rsync over SSH keys).
  3. Restart the app’s service on the server.

All with one command, no manual SSH, no passwords, no unnecessary root privileges. The non-root user vscodeuser runs its own service, boosting security, and key-based SSH lets automation run safely while eliminating brute-force password attacks. It’s a lightweight deployment pipeline using simple, fully controlled tools, and VS Code keeps the entire workflow inside your editor — convenient and error-free.

Articles you may be interested in

Discover more wonderful content

Comment