How to Enable Persistent Cross-Session Memory in DeepSeek Harness on a Server

If you use DeepSeek Harness for long-running projects, one problem appears quickly.

A conversation eventually reaches its context limit. You start a new chat the next day, but you still want DeepSeek to remember the project background, previous decisions, current progress, important parameters, and where your source materials are stored.

The goal is simple.

Conversation 1 reaches its context limit
        ↓
Important information is saved outside the chat
        ↓
Start Conversation 2
        ↓
Relevant memory is loaded automatically
        ↓
Continue working without re-uploading everything

When DeepSeek Harness runs on your local computer, this is mainly a memory-plugin problem.

When Harness runs on a remote Linux server behind Nginx, two additional issues appear frequently.

forbidden: loopback-only

and

dsh web authentication required; reopen the URL printed by dsh web.

This guide walks through the full setup, including persistent memory, Nginx reverse proxy configuration, loopback restrictions, browser authentication, and a practical test for cross-session memory.

What Persistent Memory Should Actually Save

Persistent memory works best when it preserves the information required to continue a project. Trying to copy an entire 1-million-token conversation into the next context window simply consumes the new window again.

A better model is to extract and store the state of the project.

Useful long-term memory can include

  • Project background
  • Completed work
  • Important decisions
  • Rejected approaches
  • Configuration values
  • User preferences
  • Locations of source materials
  • Current project status
  • Next actions

The workflow then looks like this.

Conversation 1
1 million tokens
        ↓
Extract important information
        ↓
Persistent memory
        ↓
Close Conversation 1
        ↓
Start Conversation 2
        ↓
Load relevant memory
        ↓
Continue the same project

The new conversation does not need every sentence from the previous session.

It needs enough information to understand the current state of the work. When a specific historical detail is required, that detail can be retrieved separately from memory or from an external document store.

Install a Long-Term Memory Plugin in DeepSeek Harness

A practical first step is to install one memory plugin and verify that it survives across sessions.

One option is

dsh-plugin-memory

Install it with

dsh plugin --profile web add dsh-plugin-memory

Then restart Harness.

dsh web

The purpose of this type of plugin is to save memory outside the active chat session so a later session can load it again.

Another option considered for this workflow is

dsh-auto-memory

That approach focuses more heavily on automatically extracting user information, project status, and ongoing memory from conversations.

If you already installed a plugin marketplace, you can also search for Memory-related plugins from the Harness interface.

The marketplace itself can be installed with

dsh plugin --profile web add dshmarket

Then start the web interface.

dsh web

Avoid Installing Several Memory Plugins at Once

Running multiple memory plugins together makes troubleshooting harder.

Several plugins may inject the same project information into the context. Their summaries may also differ, which can create conflicting project state.

Repeated memory injection also consumes tokens.

Start with one plugin and test the complete flow before adding anything else.

A minimal test looks like this.

In the first conversation, save an explicit fact.

Remember that the current test project is called Project ABC.

Create a new conversation and ask

What was the name of our previous test project?

If the new session answers Project ABC, basic cross-session memory is working.

Keep Long-Term Memory Separate from Large Source Documents

A memory plugin is suitable for project state and important conclusions.

It is less suitable for storing hundreds of PDFs, large Word files, source-code repositories, spreadsheets, or a complete archive of every previous conversation.

A more practical architecture separates memory from source materials.

DeepSeek Harness
        │
        ├─ Persistent memory
        │      │
        │      ├─ Project background
        │      ├─ Decisions
        │      ├─ Progress
        │      └─ Next actions
        │
        └─ Source materials
               │
               ├─ PDF files
               ├─ Word documents
               ├─ Source code
               └─ Historical material

Persistent memory answers questions such as what the project is doing, which decisions were made, and what should happen next.

The source-material layer keeps the original documents available when exact details are needed.

This separation lets one conversation consume a very large context window while the next conversation starts with a compact description of the project state.

Why DeepSeek Harness Shows forbidden: loopback-only

A typical remote deployment looks like this.

Browser
    ↓
https://dsh.example.com
    ↓
Nginx
    ↓
127.0.0.1:3080
    ↓
DeepSeek Harness

The important part is that Harness continues listening only on the local loopback interface.

127.0.0.1:3080

Nginx exposes HTTPS on port 443 and proxies requests to Harness locally.

A common Linux layout may use a site configuration such as

/etc/nginx/sites-available/dsh.example.com

with an enabled symlink under

/etc/nginx/sites-enabled/dsh.example.com

If Let’s Encrypt is used, certificate paths may look like

/etc/letsencrypt/live/dsh.example.com/fullchain.pem
/etc/letsencrypt/live/dsh.example.com/privkey.pem

The Harness listener should remain similar to

LISTEN 127.0.0.1:3080

There is usually no reason to expose port 3080 directly to the public internet.

How to Inspect the Existing Nginx Configuration

Before changing anything, find the current domain configuration, confirm the backend port, and verify that Harness is responding locally.

Replace dsh.example.com with your own domain.

DOMAIN="dsh.example.com"

echo "===== NGINX VERSION ====="
nginx -v 2>&1

echo
echo "===== CONFIG FILES CONTAINING DOMAIN ====="
sudo grep -Rni "$DOMAIN" /etc/nginx 2>/dev/null

echo
echo "===== ENABLED SITES ====="
sudo ls -lah /etc/nginx/sites-enabled/ 2>/dev/null

echo
echo "===== CONF.D ====="
sudo ls -lah /etc/nginx/conf.d/ 2>/dev/null

echo
echo "===== DOMAIN CONFIG ====="
sudo nginx -T 2>&1 | grep -n -B 20 -A 100 "server_name.*$DOMAIN"

echo
echo "===== PORT 3080 ====="
sudo ss -lntp | grep ':3080'

echo
echo "===== HARNESS LOCAL TEST ====="
curl -I http://127.0.0.1:3080 2>&1 | head -30

A response such as

HTTP/1.1 401 Unauthorized

does not automatically mean Harness is down.

If port 3080 is listening and the server returns an HTTP response, the request is reaching Harness.

If the browser later reports that dsh web authentication is required, that provides another indication that the web service itself is running.

Why the Original Nginx Host Header Can Trigger the Loopback Error

A common reverse-proxy configuration contains this line.

proxy_set_header Host $host;

When the browser opens

https://dsh.example.com

Nginx forwards the same hostname upstream.

Harness therefore sees

dsh.example.com

as the Host value.

Some settings, credentials, and plugin-related functions may then return

forbidden: loopback-only

One way to make the upstream request use the local Harness authority is to send 127.0.0.1:3080 to the backend.

Back up the current site configuration first.

DOMAIN="dsh.example.com"

sudo cp "/etc/nginx/sites-available/$DOMAIN" \
"/etc/nginx/sites-available/$DOMAIN.bak-$(date +%Y%m%d-%H%M%S)"

Confirm the backup before editing.

ls -lh /etc/nginx/sites-available/dsh.example.com*

Example Nginx Configuration for DeepSeek Harness

The following example uses dsh.example.com as a placeholder.

Replace it with your own hostname and confirm that the certificate paths match your server.

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name dsh.example.com;

    ssl_certificate /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    client_max_body_size 500m;

    location / {
        proxy_pass http://127.0.0.1:3080;
        proxy_http_version 1.1;

        proxy_set_header Host 127.0.0.1:3080;
        proxy_set_header Origin http://127.0.0.1:3080;
        proxy_set_header Referer http://127.0.0.1:3080/;

        proxy_set_header Sec-Fetch-Site same-origin;
        proxy_set_header Sec-Fetch-Mode cors;
        proxy_set_header Sec-Fetch-Dest empty;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_connect_timeout 60s;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;

        proxy_request_buffering off;
        proxy_buffering off;
    }
}

server {
    listen 80;
    listen [::]:80;

    server_name dsh.example.com;

    return 301 https://dsh.example.com$request_uri;
}

Open the site configuration with an editor.

sudo nano /etc/nginx/sites-available/dsh.example.com

After saving the file, validate the Nginx configuration.

sudo nginx -t

A successful result should look similar to

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reload Nginx only after the syntax test succeeds.

sudo systemctl reload nginx

Check its status afterward.

sudo systemctl status nginx --no-pager

Keep Harness bound to

127.0.0.1:3080

Do not change it to

0.0.0.0:3080

simply to make remote access easier. In this architecture, Nginx is already responsible for public access.

Fix dsh web authentication required

After the loopback issue is addressed, the browser may show

dsh web authentication required; reopen the URL printed by dsh web.

At this point the reverse proxy is reaching Harness, but the browser has not completed the dsh web authentication flow.

When dsh web starts, it prints a URL containing a token.

It may look like this.

http://127.0.0.1:3080/?token=<REDACTED_TOKEN>

A remote browser cannot open the server’s own 127.0.0.1.

Keep the token value and replace the local address with the public HTTPS hostname.

If the server prints

http://127.0.0.1:3080/?token=<REDACTED_TOKEN>

open this in the browser.

https://dsh.example.com/?token=<REDACTED_TOKEN>

After the initial authentication succeeds, the browser can continue using

https://dsh.example.com

Treat the token as sensitive information.

Do not publish it in screenshots, logs, articles, repositories, or support messages.

How to Find the DeepSeek Harness Authentication URL

If Harness is running in the background, start by checking the system journal.

sudo journalctl --no-pager | grep 'dsh web:' | tail -20

You may see a line similar to

dsh web: http://127.0.0.1:3080/?token=<REDACTED_TOKEN>

Replace only the local host portion when opening the URL remotely.

If the command returns nothing, identify how the Harness process was started.

First find the process listening on port 3080.

sudo ss -lntp | grep ':3080'

The output may contain something similar to

users:(("node",pid=<PID>,fd=<FD>))

Use the reported PID in the following commands.

PID="<PID>"

ps -fp "$PID"

Inspect the full process command line.

sudo tr '\0' ' ' < "/proc/$PID/cmdline"
echo

Check its parent process.

ps -o pid,ppid,user,lstart,cmd -p "$PID"

Inspect the process tree.

pstree -sp "$PID"

You can also check whether the process belongs to a systemd unit.

sudo systemctl status "$PID" --no-pager 2>&1 | head -80

These checks help identify whether Harness is running through one of the following methods.

systemd
PM2
nohup
Docker
screen
tmux
direct dsh web process

Once the startup method is known, the original dsh web output can usually be found in the corresponding logs.

Add the Public Domain as a Trusted Host

If you control the Harness startup command, you can also add the public hostname as a trusted host.

dsh web \
  --no-open \
  --port 3080 \
  --trusted-host dsh.example.com \
  --trusted-host dsh.example.com:443

Trusted-host configuration and browser token authentication handle different parts of the remote-access flow.

A trusted host alone does not establish a browser authentication session.

That means the browser may still show

dsh web authentication required

until the token URL has been opened successfully.

For troubleshooting, handle each layer in order.

1. Confirm 127.0.0.1:3080 is listening
2. Confirm Nginx can proxy to Harness
3. Resolve loopback-related access
4. Complete browser token authentication
5. Test the memory plugin

Changing several layers at the same time makes it much harder to identify which part is failing.

How to Test Cross-Session Memory Properly

Once Nginx and browser authentication are working, test the actual requirement.

In Conversation A, save several explicit pieces of project state.

Please remember the following information.

The current project is called Project ABC.
The server deployment and Nginx reverse proxy are complete.
The next task is to test persistent cross-session memory.

Confirm that the memory plugin has saved the information.

Then create Conversation B and ask

What is the current project called?
What work have we already completed?
What should we do next?

If the new conversation can answer those questions without receiving the information again, the persistent-memory path is working.

Conversation A
    ↓
Memory plugin
    ↓
Persistent server-side memory
    ↓
Close old conversation
    ↓
Create Conversation B
    ↓
Load project memory
    ↓
Continue working

This is the behavior that matters.

The old conversation can reach its context limit while the next session still understands the project state.

Make Sure Memory Survives Server Restarts

When Harness is installed directly on a Linux host, memory files stored on the server normally remain available as long as the underlying directories are preserved.

Docker adds another failure mode.

If memory, workspace, session, or plugin data exists only inside the container filesystem, deleting or recreating the container can remove it.

A server deployment therefore needs several forms of persistence.

Data What needs to persist
Active chat context Limited by the model context window
Long-term memory Memory plugin data
PDFs and source documents Persistent document storage
Harness configuration Server-side configuration files
Docker data Mounted persistent volumes
Browser access dsh web authentication session

For long-running projects, chat history alone is not enough. The project state and source material need storage outside the active model context.

What to Remove Before Publishing Your Configuration

Server troubleshooting posts can expose more information than expected.

Before publishing screenshots, logs, or configuration files, remove or replace the following values.

  • Real production domains
  • Public IP addresses
  • SSH usernames
  • API keys
  • Harness startup tokens
  • Browser cookies
  • Authorization headers
  • Cloudflare tokens
  • Database passwords
  • Private keys
  • .env contents
  • Internal hostnames
  • Environment-specific private paths
  • Current process IDs when they are unnecessary

Generic system paths can usually remain in technical documentation.

Examples include

/etc/nginx/sites-available/
/etc/letsencrypt/live/
127.0.0.1:3080

Use placeholders for environment-specific values.

dsh.example.com
203.0.113.x
<SSH_USER>
<PID>
<API_KEY>
<REDACTED_TOKEN>

DeepSeek Harness Persistent Memory Setup Checklist

Use this checklist when deploying Harness behind Nginx.

  • Keep Harness bound to 127.0.0.1:3080
  • Expose the service through HTTPS on Nginx
  • Back up the Nginx site configuration before editing
  • Keep the upstream target on http://127.0.0.1:3080
  • Adjust upstream Host, Origin, and Referer when required by the loopback setup
  • Run sudo nginx -t
  • Reload Nginx only after the test succeeds
  • Find the token URL printed by dsh web
  • Replace the local address with your HTTPS hostname
  • Open the token URL once to establish browser authentication
  • Keep the token private
  • Install one long-term memory plugin first
  • Save an explicit test memory in the old session
  • Create a new session and verify memory recall
  • Keep large PDFs, codebases, and original documents outside the chat context
  • Persist memory and workspace data when using Docker

FAQ

Why does DeepSeek Harness return 401 Unauthorized on 127.0.0.1:3080

A 401 response can mean that Harness is running but the request has not completed the browser authentication flow.

If the port is listening and the server responds to HTTP requests, continue by checking the dsh web authentication URL.

Why does the web interface work while Settings shows forbidden: loopback-only

A common cause is that Nginx forwards the public hostname to Harness.

For example

proxy_set_header Host $host;

sends the external hostname upstream.

The configuration in this guide uses

proxy_set_header Host 127.0.0.1:3080;

along with corresponding Origin and Referer headers.

Why did authentication required appear after fixing Nginx

That usually means the request reached Harness successfully and the browser now needs to complete the dsh web token authentication step.

Find the startup URL containing ?token= and open the equivalent URL through your HTTPS domain.

Should the dsh web token be stored in the public Nginx configuration

No.

Treat it as sensitive authentication material. Keep it out of public configuration examples, screenshots, repositories, and documentation.

Does a memory plugin save the full 1-million-token conversation

The useful goal is to preserve the information required for future sessions.

Project background, decisions, progress, and next actions are good candidates for long-term memory.

Large original documents and historical source material should remain in external persistent storage and be retrieved when needed.

Do I need to open the token URL every time I create a new conversation

Creating a new Harness conversation and authenticating the browser are separate actions.

As long as the browser authentication session remains valid, starting a new chat does not require another token login.

Should DeepSeek Harness listen on 0.0.0.0:3080

The deployment described here does not require it.

Keeping Harness on 127.0.0.1:3080 allows Nginx to remain the public entry point while the Harness backend stays local to the server.

How do I know that persistent memory is really working

Do not use plugin installation status as the final test.

Save a specific project name, completed task, and next action in one conversation. Create a new conversation and ask for those values without providing them again.

If the new session retrieves them correctly, cross-session memory is working.